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