libavcodec/snappy.c
2ecfd451
 /*
  * Snappy decompression algorithm
  * Copyright (c) 2015 Luca Barbato
  *
9e5b0f07
  * This file is part of FFmpeg.
2ecfd451
  *
9e5b0f07
  * FFmpeg is free software; you can redistribute it and/or
2ecfd451
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
9e5b0f07
  * FFmpeg is distributed in the hope that it will be useful,
2ecfd451
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
9e5b0f07
  * License along with FFmpeg; if not, write to the Free Software
2ecfd451
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 #include "libavutil/mem.h"
 
 #include "bytestream.h"
 #include "snappy.h"
 
 enum {
     SNAPPY_LITERAL,
     SNAPPY_COPY_1,
     SNAPPY_COPY_2,
     SNAPPY_COPY_4,
 };
 
 static int64_t bytestream2_get_levarint(GetByteContext *gb)
 {
     uint64_t val = 0;
     int shift = 0;
     int tmp;
 
     do {
         tmp = bytestream2_get_byte(gb);
         val |= (tmp & 127) << shift;
         shift += 7;
     } while (tmp & 128);
 
     return val;
 }
 
 static int snappy_literal(GetByteContext *gb, uint8_t *p, int size, int val)
 {
     unsigned int len = 1;
 
     switch (val) {
     case 63:
         len += bytestream2_get_le32(gb);
         break;
     case 62:
         len += bytestream2_get_le24(gb);
         break;
     case 61:
         len += bytestream2_get_le16(gb);
         break;
     case 60:
         len += bytestream2_get_byte(gb);
         break;
     default: // val < 60
         len += val;
     }
 
     if (size < len)
         return AVERROR_INVALIDDATA;
 
     bytestream2_get_buffer(gb, p, len);
 
     return len;
 }
 
 static int snappy_copy(uint8_t *start, uint8_t *p, int size,
                        unsigned int off, int len)
 {
     uint8_t *q;
     int i;
     if (off > p - start || size < len)
         return AVERROR_INVALIDDATA;
 
     q = p - off;
 
     for (i = 0; i < len; i++)
         p[i] = q[i];
 
     return len;
 }
 
 static int snappy_copy1(GetByteContext *gb, uint8_t *start, uint8_t *p,
                         int size, int val)
 {
     int len          = 4 + (val & 0x7);
     unsigned int off = bytestream2_get_byte(gb) | (val & 0x38) << 5;
 
     return snappy_copy(start, p, size, off, len);
 }
 
 static int snappy_copy2(GetByteContext *gb, uint8_t *start, uint8_t *p,
                         int size, int val)
 {
     int len          = 1 + val;
     unsigned int off = bytestream2_get_le16(gb);
 
     return snappy_copy(start, p, size, off, len);
 }
 
 static int snappy_copy4(GetByteContext *gb, uint8_t *start, uint8_t *p,
                         int size, int val)
 {
     int len          = 1 + val;
     unsigned int off = bytestream2_get_le32(gb);
 
     return snappy_copy(start, p, size, off, len);
 }
 
 static int64_t decode_len(GetByteContext *gb)
 {
     int64_t len = bytestream2_get_levarint(gb);
 
     if (len < 0 || len > UINT_MAX)
         return AVERROR_INVALIDDATA;
 
     return len;
 }
 
17ee24af
 int64_t ff_snappy_peek_uncompressed_length(GetByteContext *gb)
 {
     int pos = bytestream2_get_bytes_left(gb);
     int64_t len = decode_len(gb);
 
     bytestream2_seek(gb, -pos, SEEK_END);
 
     return len;
 }
 
 int ff_snappy_uncompress(GetByteContext *gb, uint8_t *buf, int64_t *size)
2ecfd451
 {
     int64_t len = decode_len(gb);
     int ret     = 0;
     uint8_t *p;
 
     if (len < 0)
         return len;
 
17ee24af
     if (len > *size)
         return AVERROR_BUFFER_TOO_SMALL;
2ecfd451
 
     *size = len;
17ee24af
     p     = buf;
2ecfd451
 
     while (bytestream2_get_bytes_left(gb) > 0) {
         uint8_t s = bytestream2_get_byte(gb);
         int val   = s >> 2;
 
         switch (s & 0x03) {
         case SNAPPY_LITERAL:
             ret = snappy_literal(gb, p, len, val);
             break;
         case SNAPPY_COPY_1:
17ee24af
             ret = snappy_copy1(gb, buf, p, len, val);
2ecfd451
             break;
         case SNAPPY_COPY_2:
17ee24af
             ret = snappy_copy2(gb, buf, p, len, val);
2ecfd451
             break;
         case SNAPPY_COPY_4:
17ee24af
             ret = snappy_copy4(gb, buf, p, len, val);
2ecfd451
             break;
         }
 
         if (ret < 0)
             return ret;
 
         p   += ret;
         len -= ret;
     }
 
     return 0;
 }