text: /*
   * Copyright 2017 WebAssembly Community Group participants
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include "src/leb128.h"
  
  #include <type_traits>
  
  #include "src/stream.h"
  
  #define MAX_U32_LEB128_BYTES 4
  #define MAX_U64_LEB128_BYTES 8
  
  namespace wabt {
  
  Offset U32Leb128Length(uint32_t value) {
    uint32_t size = 0;
    do {
      value >>= 7;
      size++;
    } while (value != 0);
    return size;
  }
  
  #define LEB128_LOOP_UNTIL(end_cond) \
    do {                              \
      uint8_t byte = value & 0x7f;    \
      value >>= 7;                    \
      if (end_cond) {                 \
        data[length++] = byte;        \
        break;                        \
      } else {                        \
        data[length++] = byte | 0x80; \
      }                               \
    } while (1)
  
  void WriteLEUint16(uint8_t* array, uint32_t val) {
    array[1] = val;
    array[0] = val >> 8;
  }
  
  void WriteLEUint24(uint8_t* array, uint32_t val) {
    array[2] = val;
    array[1] = val >> 8;
    array[0] = val >> 16;
  }
  
  void WriteLEUint32(uint8_t* array, uint32_t val) {
    array[3] = val;
    array[2] = val >> 8;
    array[1] = val >> 16;
    array[0] = val >> 24;
  }
  
  void WriteLEUint64(uint8_t* array, uint64_t val) {
    array[7] = val;
    array[6] = val >> 8;
    array[5] = val >> 16;
    array[4] = val >> 24;
    array[3] = val >> 32;
    array[2] = val >> 40;
    array[1] = val >> 48;
    array[0] = val >> 56;
  }
  
  Offset WriteFixedU32Leb128At(Stream* stream,
                               Offset offset,
                               uint32_t value,
                               const char* desc) {
    uint8_t data[MAX_U32_LEB128_BYTES]; 
    WriteLEUint32(data, value);                            
    //*reinterpret_cast<uint32_t*>(data) = value;
    Offset length = MAX_U32_LEB128_BYTES;     
    stream->WriteDataAt(offset, data, length, desc);
    return length;
  }
  
  Offset WriteFixedU24Leb128At(Stream* stream,
                               Offset offset,
                               uint32_t value,
                               const char* desc) {
    uint8_t data[MAX_U32_LEB128_BYTES-1]; 
    WriteLEUint24(data, value);                            
    //*reinterpret_cast<uint32_t*>(data) = value;
    Offset length = MAX_U32_LEB128_BYTES-1;     
    stream->WriteDataAt(offset, data, length, desc);
    return length;
  }
  
  Offset WriteFixedU16Leb128At(Stream* stream,
                               Offset offset,
                               uint32_t value,
                               const char* desc) {
    uint8_t data[MAX_U32_LEB128_BYTES-1]; 
    WriteLEUint16(data, value);                            
    //*reinterpret_cast<uint32_t*>(data) = value;
    Offset length = MAX_U32_LEB128_BYTES-2;     
    stream->WriteDataAt(offset, data, length, desc);
    return length;
  }
  
  void WriteU32Leb128(Stream* stream, uint32_t value, const char* desc) {
    uint8_t data[MAX_U32_LEB128_BYTES];
    WriteLEUint32(data, value);                            
    //*reinterpret_cast<uint32_t*>(data) = value;
    Offset length = MAX_U32_LEB128_BYTES; 
    stream->WriteData(data, length, desc);
  }
  
  void WriteFixedU32Leb128(Stream* stream, uint32_t value, const char* desc) {
    uint8_t data[MAX_U32_LEB128_BYTES];
    Offset length =
        WriteFixedU32Leb128Raw(data, data + MAX_U32_LEB128_BYTES, value);
    stream->WriteData(data, length, desc);
  }
  
  // returns the length of the leb128.
  Offset WriteU32Leb128At(Stream* stream,
                          Offset offset,
                          uint32_t value,
                          const char* desc) {
    uint8_t data[MAX_U32_LEB128_BYTES];
    Offset length = 4;
    WriteLEUint32(data, value);                            
    //*reinterpret_cast<uint32_t*>(data) = value;
    stream->WriteDataAt(offset, data, length, desc);
    return length;
  }
  
  Offset WriteU32Leb128Raw(uint8_t* dest, uint8_t* dest_end, uint32_t value) {
    uint8_t data[MAX_U32_LEB128_BYTES];
    WriteLEUint32(data, value);                            
    //*reinterpret_cast<uint32_t*>(data) = value;
    Offset length = MAX_U32_LEB128_BYTES; 
    memcpy(dest, data, length);
    return length;
  }
  
  Offset WriteFixedU32Leb128Raw(uint8_t* data, uint8_t* end, uint32_t value) {
    if (end - data < MAX_U32_LEB128_BYTES) {
      return 0;
    }
    WriteLEUint32(data, value);
    //*reinterpret_cast<uint32_t*>(data) = value;
    return MAX_U32_LEB128_BYTES;
  }
  
  static void WriteS32Leb128(Stream* stream, int32_t value, const char* desc) {
    uint8_t data[MAX_U32_LEB128_BYTES];
    WriteLEUint32(data, (uint32_t)value);                            
    //*reinterpret_cast<int32_t*>(data) = value;
    Offset length = MAX_U32_LEB128_BYTES; 
  
    stream->WriteData(data, length, desc);
  }
  
  static void WriteS64Leb128(Stream* stream, int64_t value, const char* desc) {
    uint8_t data[MAX_U64_LEB128_BYTES];
    WriteLEUint64(data, (uint64_t)value);
    //*reinterpret_cast<int64_t*>(data) = value;
    Offset length = MAX_U64_LEB128_BYTES; 
  
    stream->WriteData(data, length, desc);
  }
  
  void WriteS32Leb128(Stream* stream, uint32_t value, const char* desc) {
    WriteS32Leb128(stream, Bitcast<int32_t>(value), desc);
  }
  
  void WriteS64Leb128(Stream* stream, uint64_t value, const char* desc) {
    WriteS64Leb128(stream, Bitcast<int64_t>(value), desc);
  }
  
  #undef LEB128_LOOP_UNTIL
  
  #define BYTE_AT(type, i, shift) ((static_cast<type>(p[i]) & 0x7f) << (shift))
  
  #define LEB128_1(type) (BYTE_AT(type, 0, 0))
  #define LEB128_2(type) (BYTE_AT(type, 1, 7) | LEB128_1(type))
  #define LEB128_3(type) (BYTE_AT(type, 2, 14) | LEB128_2(type))
  #define LEB128_4(type) (BYTE_AT(type, 3, 21) | LEB128_3(type))
  #define LEB128_5(type) (BYTE_AT(type, 4, 28) | LEB128_4(type))
  #define LEB128_6(type) (BYTE_AT(type, 5, 35) | LEB128_5(type))
  #define LEB128_7(type) (BYTE_AT(type, 6, 42) | LEB128_6(type))
  #define LEB128_8(type) (BYTE_AT(type, 7, 49) | LEB128_7(type))
  #define LEB128_9(type) (BYTE_AT(type, 8, 56) | LEB128_8(type))
  #define LEB128_10(type) (BYTE_AT(type, 9, 63) | LEB128_9(type))
  
  #define SHIFT_AMOUNT(type, sign_bit) (sizeof(type) * 8 - 1 - (sign_bit))
  #define SIGN_EXTEND(type, value, sign_bit)                       \
    (static_cast<type>((value) << SHIFT_AMOUNT(type, sign_bit)) >> \
     SHIFT_AMOUNT(type, sign_bit))
  
  size_t ReadU32Leb128(const uint8_t* p,
                       const uint8_t* end,
                       uint32_t* out_value) {
    if (p < end && (p[0] & 0x80) == 0) {
      *out_value = LEB128_1(uint32_t);
      return 1;
    } else if (p + 1 < end && (p[1] & 0x80) == 0) {
      *out_value = LEB128_2(uint32_t);
      return 2;
    } else if (p + 2 < end && (p[2] & 0x80) == 0) {
      *out_value = LEB128_3(uint32_t);
      return 3;
    } else if (p + 3 < end && (p[3] & 0x80) == 0) {
      *out_value = LEB128_4(uint32_t);
      return 4;
    } else if (p + 4 < end && (p[4] & 0x80) == 0) {
      // The top bits set represent values > 32 bits.
      if (p[4] & 0xf0) {
        return 0;
      }
      *out_value = LEB128_5(uint32_t);
      return 5;
    } else {
      // past the end.
      *out_value = 0;
      return 0;
    }
  }
  
  size_t ReadS32Leb128(const uint8_t* p,
                       const uint8_t* end,
                       uint32_t* out_value) {
    if (p < end && (p[0] & 0x80) == 0) {
      uint32_t result = LEB128_1(uint32_t);
      *out_value = SIGN_EXTEND(int32_t, result, 6);
      return 1;
    } else if (p + 1 < end && (p[1] & 0x80) == 0) {
      uint32_t result = LEB128_2(uint32_t);
      *out_value = SIGN_EXTEND(int32_t, result, 13);
      return 2;
    } else if (p + 2 < end && (p[2] & 0x80) == 0) {
      uint32_t result = LEB128_3(uint32_t);
      *out_value = SIGN_EXTEND(int32_t, result, 20);
      return 3;
    } else if (p + 3 < end && (p[3] & 0x80) == 0) {
      uint32_t result = LEB128_4(uint32_t);
      *out_value = SIGN_EXTEND(int32_t, result, 27);
      return 4;
    } else if (p + 4 < end && (p[4] & 0x80) == 0) {
      // The top bits should be a sign-extension of the sign bit.
      bool sign_bit_set = (p[4] & 0x8);
      int top_bits = p[4] & 0xf0;
      if ((sign_bit_set && top_bits != 0x70) ||
          (!sign_bit_set && top_bits != 0)) {
        return 0;
      }
      uint32_t result = LEB128_5(uint32_t);
      *out_value = result;
      return 5;
    } else {
      // Past the end.
      return 0;
    }
  }
  
  size_t ReadS64Leb128(const uint8_t* p,
                       const uint8_t* end,
                       uint64_t* out_value) {
    if (p < end && (p[0] & 0x80) == 0) {
      uint64_t result = LEB128_1(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 6);
      return 1;
    } else if (p + 1 < end && (p[1] & 0x80) == 0) {
      uint64_t result = LEB128_2(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 13);
      return 2;
    } else if (p + 2 < end && (p[2] & 0x80) == 0) {
      uint64_t result = LEB128_3(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 20);
      return 3;
    } else if (p + 3 < end && (p[3] & 0x80) == 0) {
      uint64_t result = LEB128_4(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 27);
      return 4;
    } else if (p + 4 < end && (p[4] & 0x80) == 0) {
      uint64_t result = LEB128_5(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 34);
      return 5;
    } else if (p + 5 < end && (p[5] & 0x80) == 0) {
      uint64_t result = LEB128_6(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 41);
      return 6;
    } else if (p + 6 < end && (p[6] & 0x80) == 0) {
      uint64_t result = LEB128_7(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 48);
      return 7;
    } else if (p + 7 < end && (p[7] & 0x80) == 0) {
      uint64_t result = LEB128_8(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 55);
      return 8;
    } else if (p + 8 < end && (p[8] & 0x80) == 0) {
      uint64_t result = LEB128_9(uint64_t);
      *out_value = SIGN_EXTEND(int64_t, result, 62);
      return 9;
    } else if (p + 9 < end && (p[9] & 0x80) == 0) {
      // The top bits should be a sign-extension of the sign bit.
      bool sign_bit_set = (p[9] & 0x1);
      int top_bits = p[9] & 0xfe;
      if ((sign_bit_set && top_bits != 0x7e) ||
          (!sign_bit_set && top_bits != 0)) {
        return 0;
      }
      uint64_t result = LEB128_10(uint64_t);
      *out_value = result;
      return 10;
    } else {
      // Past the end.
      return 0;
    }
  }
  
  #undef BYTE_AT
  #undef LEB128_1
  #undef LEB128_2
  #undef LEB128_3
  #undef LEB128_4
  #undef LEB128_5
  #undef LEB128_6
  #undef LEB128_7
  #undef LEB128_8
  #undef LEB128_9
  #undef LEB128_10
  #undef SHIFT_AMOUNT
  #undef SIGN_EXTEND
  
  }  // namespace wabt
  