2 * Copyright 2016-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/experimental/bser/Bser.h>
19 #include <folly/String.h>
20 #include <folly/io/Cursor.h>
22 using namespace folly;
23 using folly::io::Cursor;
27 static dynamic parseBser(Cursor& curs);
29 template <typename... ARGS>
30 [[noreturn]] static void throwDecodeError(Cursor& curs, ARGS&&... args) {
31 throw BserDecodeError(folly::to<std::string>(
32 std::forward<ARGS>(args)...,
35 " bytes remaining in cursor"));
38 static int64_t decodeInt(Cursor& curs) {
39 auto enc = (BserType)curs.read<int8_t>();
42 return curs.read<int8_t>();
44 return curs.read<int16_t>();
46 return curs.read<int32_t>();
48 return curs.read<int64_t>();
51 curs, "invalid integer encoding detected (", (int8_t)enc, ")");
55 static std::string decodeString(Cursor& curs) {
56 auto len = decodeInt(curs);
60 throw std::range_error("string length must not be negative");
62 str.reserve(size_t(len));
64 // peekBytes will advance over any "empty" IOBuf elements until
65 // it reaches the next one with data, so do that to obtain the
66 // true remaining length.
67 size_t available = curs.peekBytes().size();
68 while (available < (size_t)len) {
70 // Saw this case when we decodeHeader was returning the incorrect length
71 // and we were splitting off too few bytes from the IOBufQueue
74 "no data available while decoding a string, header was "
75 "not decoded properly");
77 str.append(reinterpret_cast<const char*>(curs.data()), available);
78 curs.skipAtMost(available);
80 available = curs.peekBytes().size();
83 str.append(reinterpret_cast<const char*>(curs.data()), size_t(len));
84 curs.skipAtMost(size_t(len));
88 static dynamic decodeArray(Cursor& curs) {
89 dynamic arr = dynamic::array();
90 auto size = decodeInt(curs);
92 arr.push_back(parseBser(curs));
97 static dynamic decodeObject(Cursor& curs) {
98 dynamic obj = dynamic::object;
99 auto size = decodeInt(curs);
101 if ((BserType)curs.read<int8_t>() != BserType::String) {
102 throwDecodeError(curs, "expected String");
104 auto key = decodeString(curs);
105 obj[key] = parseBser(curs);
110 static dynamic decodeTemplate(Cursor& curs) {
111 dynamic arr = folly::dynamic::array;
113 // List of property names
114 if ((BserType)curs.read<int8_t>() != BserType::Array) {
115 throw std::runtime_error("Expected array encoding for property names");
117 auto names = decodeArray(curs);
119 auto size = decodeInt(curs);
122 dynamic obj = dynamic::object;
124 for (auto& name : names) {
125 auto bytes = curs.peekBytes();
126 if ((BserType)bytes.at(0) == BserType::Skip) {
127 obj[name.getString()] = nullptr;
132 obj[name.getString()] = parseBser(curs);
135 arr.push_back(std::move(obj));
141 static dynamic parseBser(Cursor& curs) {
142 switch ((BserType)curs.read<int8_t>()) {
144 return curs.read<int8_t>();
145 case BserType::Int16:
146 return curs.read<int16_t>();
147 case BserType::Int32:
148 return curs.read<int32_t>();
149 case BserType::Int64:
150 return curs.read<int64_t>();
151 case BserType::Real: {
153 curs.pull((void*)&dval, sizeof(dval));
160 case BserType::False:
162 case BserType::String:
163 return decodeString(curs);
164 case BserType::Array:
165 return decodeArray(curs);
166 case BserType::Object:
167 return decodeObject(curs);
168 case BserType::Template:
169 return decodeTemplate(curs);
171 throw std::runtime_error(
172 "Skip not valid at this location in the bser stream");
174 throw std::runtime_error("invalid bser encoding");
178 static size_t decodeHeader(Cursor& curs) {
179 char header[sizeof(kMagic)];
180 curs.pull(header, sizeof(header));
181 if (memcmp(header, kMagic, sizeof(kMagic))) {
182 throw std::runtime_error("invalid BSER magic header");
185 auto enc = (BserType)curs.peekBytes().at(0);
191 case BserType::Int16:
194 case BserType::Int32:
197 case BserType::Int64:
204 return int_size + 3 /* magic + int type */ + decodeInt(curs);
207 size_t decodePduLength(const folly::IOBuf* buf) {
209 return decodeHeader(curs);
212 folly::dynamic parseBser(const IOBuf* buf) {
216 return parseBser(curs);
219 folly::dynamic parseBser(ByteRange str) {
220 auto buf = IOBuf::wrapBuffer(str.data(), str.size());
221 return parseBser(&*buf);
224 folly::dynamic parseBser(StringPiece str) {
225 return parseBser(ByteRange((uint8_t*)str.data(), str.size()));