1 // Copyright (c) 2016 The WebM project authors. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the LICENSE file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 #ifndef SRC_INT_PARSER_H_ 9 #define SRC_INT_PARSER_H_ 10 11 #include <cassert> 12 #include <cstdint> 13 #include <limits> 14 #include <type_traits> 15 16 #include "src/element_parser.h" 17 #include "src/parser_utils.h" 18 #include "webm/callback.h" 19 #include "webm/element.h" 20 #include "webm/reader.h" 21 #include "webm/status.h" 22 23 namespace webm { 24 25 // Parses an EBML signed/unsigned int from a byte stream. 26 // Spec reference: 27 // http://matroska.org/technical/specs/index.html#EBML_ex 28 // https://github.com/Matroska-Org/ebml-specification/blob/master/specification.markdown#element-data-size 29 // https://github.com/Matroska-Org/ebml-specification/blob/master/specification.markdown#ebml-element-types 30 template <typename T> 31 class IntParser : public ElementParser { 32 public: 33 static_assert( 34 std::is_same<T, std::int64_t>::value || 35 std::is_same<T, std::uint64_t>::value || 36 (std::is_enum<T>::value && sizeof(T) == 8), 37 "T must be either std::int64_t, std::uint64_t, or a 64-bit enum"); 38 39 // Constructs a new parser which will use the given default_value as the 40 // value for the element if its size is zero. Defaults to the value zero (as 41 // the EBML spec indicates). default_value_(default_value)42 explicit IntParser(T default_value = {}) : default_value_(default_value) {} 43 44 IntParser(IntParser&&) = default; 45 IntParser& operator=(IntParser&&) = default; 46 47 IntParser(const IntParser&) = delete; 48 IntParser& operator=(const IntParser&) = delete; 49 Init(const ElementMetadata & metadata,std::uint64_t max_size)50 Status Init(const ElementMetadata& metadata, 51 std::uint64_t max_size) override { 52 assert(metadata.size == kUnknownElementSize || metadata.size <= max_size); 53 54 // Matroska requires integers to be 0-8 bytes in size. 55 if (metadata.size > 8) { 56 return Status(Status::kInvalidElementSize); 57 } 58 59 size_ = num_bytes_remaining_ = static_cast<int>(metadata.size); 60 61 if (metadata.size == 0) { 62 value_ = default_value_; 63 } else { 64 value_ = {}; 65 } 66 67 return Status(Status::kOkCompleted); 68 } 69 Feed(Callback * callback,Reader * reader,std::uint64_t * num_bytes_read)70 Status Feed(Callback* callback, Reader* reader, 71 std::uint64_t* num_bytes_read) override { 72 assert(callback != nullptr); 73 assert(reader != nullptr); 74 assert(num_bytes_read != nullptr); 75 76 const Status status = AccumulateIntegerBytes(num_bytes_remaining_, reader, 77 &value_, num_bytes_read); 78 num_bytes_remaining_ -= static_cast<int>(*num_bytes_read); 79 80 // Sign extend the integer if it's a negative value. EBML allows for 81 // negative integers to drop superfluous sign bytes (i.e. -1 can be encoded 82 // as 0xFF instead of 0xFFFFFFFFFFFFFFFF). 83 if (std::is_signed<T>::value && num_bytes_remaining_ == 0 && size_ > 0) { 84 std::uint64_t sign_bits = std::numeric_limits<std::uint64_t>::max() 85 << (8 * size_ - 1); 86 std::uint64_t unsigned_value = static_cast<std::uint64_t>(value_); 87 if (unsigned_value & sign_bits) { 88 value_ = static_cast<T>(unsigned_value | sign_bits); 89 } 90 } 91 92 return status; 93 } 94 95 // Gets the parsed int. This must not be called until the parse had been 96 // successfully completed. value()97 T value() const { 98 assert(num_bytes_remaining_ == 0); 99 return value_; 100 } 101 102 // Gets the parsed int. This must not be called until the parse had been 103 // successfully completed. mutable_value()104 T* mutable_value() { 105 assert(num_bytes_remaining_ == 0); 106 return &value_; 107 } 108 109 private: 110 T value_; 111 T default_value_; 112 int num_bytes_remaining_ = -1; 113 int size_; 114 }; 115 116 using SignedIntParser = IntParser<std::int64_t>; 117 using UnsignedIntParser = IntParser<std::uint64_t>; 118 119 } // namespace webm 120 121 #endif // SRC_INT_PARSER_H_ 122