add Cursor::peekBytes()
[folly.git] / folly / experimental / bser / Load.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include "Bser.h"
17 #include <folly/io/Cursor.h>
18 #include <folly/String.h>
19
20 using namespace folly;
21 using folly::io::Cursor;
22
23 namespace folly {
24 namespace bser {
25 static dynamic parseBser(Cursor& curs);
26
27 template <typename... ARGS>
28 [[noreturn]] static void throwDecodeError(Cursor& curs, ARGS&&... args) {
29   throw BserDecodeError(folly::to<std::string>(std::forward<ARGS>(args)...,
30                                                " with ",
31                                                curs.length(),
32                                                " bytes remaining in cursor"));
33 }
34
35 static int64_t decodeInt(Cursor& curs) {
36   auto enc = (BserType)curs.read<int8_t>();
37   switch (enc) {
38     case BserType::Int8:
39       return curs.read<int8_t>();
40     case BserType::Int16:
41       return curs.read<int16_t>();
42     case BserType::Int32:
43       return curs.read<int32_t>();
44     case BserType::Int64:
45       return curs.read<int64_t>();
46     default:
47       throwDecodeError(
48           curs, "invalid integer encoding detected (", (int8_t)enc, ")");
49   }
50 }
51
52 static std::string decodeString(Cursor& curs) {
53   auto len = decodeInt(curs);
54   std::string str;
55
56   if (len < 0) {
57     throw std::range_error("string length must not be negative");
58   }
59   str.reserve(len);
60
61   size_t available = curs.length();
62   while (available < (size_t)len) {
63     if (available == 0) {
64       // Saw this case when we decodeHeader was returning the incorrect length
65       // and we were splitting off too few bytes from the IOBufQueue
66       throwDecodeError(curs,
67                        "no data available while decoding a string, header was "
68                        "not decoded properly");
69     }
70     str.append(reinterpret_cast<const char*>(curs.data()), available);
71     curs.skipAtMost(available);
72     len -= available;
73     available = curs.length();
74   }
75
76   str.append(reinterpret_cast<const char*>(curs.data()), len);
77   curs.skipAtMost(len);
78   return str;
79 }
80
81 static dynamic decodeArray(Cursor& curs) {
82   dynamic arr{};
83   auto size = decodeInt(curs);
84   while (size-- > 0) {
85     arr.push_back(parseBser(curs));
86   }
87   return arr;
88 }
89
90 static dynamic decodeObject(Cursor& curs) {
91   dynamic obj = dynamic::object;
92   auto size = decodeInt(curs);
93   while (size-- > 0) {
94     if ((BserType)curs.read<int8_t>() != BserType::String) {
95       throwDecodeError(curs, "expected String");
96     }
97     auto key = decodeString(curs);
98     obj[key] = parseBser(curs);
99   }
100   return obj;
101 }
102
103 static dynamic decodeTemplate(Cursor& curs) {
104   std::vector<dynamic> arr;
105
106   // List of property names
107   if ((BserType)curs.read<int8_t>() != BserType::Array) {
108     throw std::runtime_error("Expected array encoding for property names");
109   }
110   auto names = decodeArray(curs);
111
112   auto size = decodeInt(curs);
113   arr.reserve(size);
114
115   while (size-- > 0) {
116     dynamic obj = dynamic::object;
117
118     for (auto& name : names) {
119       auto bytes = curs.peekBytes();
120       if ((BserType)bytes.at(0) == BserType::Skip) {
121         obj[name.getString()] = nullptr;
122         curs.skipAtMost(1);
123         continue;
124       }
125
126       obj[name.getString()] = parseBser(curs);
127     }
128
129     arr.emplace_back(std::move(obj));
130   }
131
132   return dynamic(std::move(arr));
133 }
134
135 static dynamic parseBser(Cursor& curs) {
136   switch ((BserType)curs.read<int8_t>()) {
137     case BserType::Int8:
138       return curs.read<int8_t>();
139     case BserType::Int16:
140       return curs.read<int16_t>();
141     case BserType::Int32:
142       return curs.read<int32_t>();
143     case BserType::Int64:
144       return curs.read<int64_t>();
145     case BserType::Real: {
146       double dval;
147       curs.pull((void*)&dval, sizeof(dval));
148       return dval;
149     }
150     case BserType::Null:
151       return nullptr;
152     case BserType::True:
153       return (bool)true;
154     case BserType::False:
155       return (bool)false;
156     case BserType::String:
157       return decodeString(curs);
158     case BserType::Array:
159       return decodeArray(curs);
160     case BserType::Object:
161       return decodeObject(curs);
162     case BserType::Template:
163       return decodeTemplate(curs);
164     case BserType::Skip:
165       throw std::runtime_error(
166           "Skip not valid at this location in the bser stream");
167     default:
168       throw std::runtime_error("invalid bser encoding");
169   }
170 }
171
172 static size_t decodeHeader(Cursor& curs) {
173   char header[sizeof(kMagic)];
174   curs.pull(header, sizeof(header));
175   if (memcmp(header, kMagic, sizeof(kMagic))) {
176     throw std::runtime_error("invalid BSER magic header");
177   }
178
179   auto enc = (BserType)curs.peekBytes().at(0);
180   size_t int_size;
181   switch (enc) {
182     case BserType::Int8:
183       int_size = 1;
184       break;
185     case BserType::Int16:
186       int_size = 2;
187       break;
188     case BserType::Int32:
189       int_size = 4;
190       break;
191     case BserType::Int64:
192       int_size = 8;
193       break;
194     default:
195       int_size = 0;
196   }
197
198   return int_size + 3 /* magic + int type */ + decodeInt(curs);
199 }
200
201 size_t decodePduLength(const folly::IOBuf* buf) {
202   Cursor curs(buf);
203   return decodeHeader(curs);
204 }
205
206 folly::dynamic parseBser(const IOBuf* buf) {
207   Cursor curs(buf);
208
209   decodeHeader(curs);
210   return parseBser(curs);
211 }
212
213 folly::dynamic parseBser(ByteRange str) {
214   auto buf = IOBuf::wrapBuffer(str.data(), str.size());
215   return parseBser(&*buf);
216 }
217
218 folly::dynamic parseBser(StringPiece str) {
219   return parseBser(ByteRange((uint8_t*)str.data(), str.size()));
220 }
221 }
222 }
223
224 /* vim:ts=2:sw=2:et:
225  */