Adds writer test case for RCU
[folly.git] / folly / experimental / bser / Load.cpp
1 /*
2  * Copyright 2016-present 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
63   // We could use Cursor::readFixedString() here, but we'd like
64   // to throw our own exception with some increased diagnostics.
65   str.resize(len);
66
67   // The start of the string data, mutable.
68   auto* dest = &str[0];
69
70   auto pulled = curs.pullAtMost(dest, len);
71   if (pulled != size_t(len)) {
72     // Saw this case when decodeHeader was returning the incorrect length
73     // and we were splitting off too few bytes from the IOBufQueue
74     throwDecodeError(
75         curs,
76         "no data available while decoding a string, header was "
77         "not decoded properly");
78   }
79
80   return str;
81 }
82
83 static dynamic decodeArray(Cursor& curs) {
84   dynamic arr = dynamic::array();
85   auto size = decodeInt(curs);
86   while (size-- > 0) {
87     arr.push_back(parseBser(curs));
88   }
89   return arr;
90 }
91
92 static dynamic decodeObject(Cursor& curs) {
93   dynamic obj = dynamic::object;
94   auto size = decodeInt(curs);
95   while (size-- > 0) {
96     if ((BserType)curs.read<int8_t>() != BserType::String) {
97       throwDecodeError(curs, "expected String");
98     }
99     auto key = decodeString(curs);
100     obj[key] = parseBser(curs);
101   }
102   return obj;
103 }
104
105 static dynamic decodeTemplate(Cursor& curs) {
106   dynamic arr = folly::dynamic::array;
107
108   // List of property names
109   if ((BserType)curs.read<int8_t>() != BserType::Array) {
110     throw std::runtime_error("Expected array encoding for property names");
111   }
112   auto names = decodeArray(curs);
113
114   auto size = decodeInt(curs);
115
116   while (size-- > 0) {
117     dynamic obj = dynamic::object;
118
119     for (auto& name : names) {
120       auto bytes = curs.peekBytes();
121       if ((BserType)bytes.at(0) == BserType::Skip) {
122         obj[name.getString()] = nullptr;
123         curs.skipAtMost(1);
124         continue;
125       }
126
127       obj[name.getString()] = parseBser(curs);
128     }
129
130     arr.push_back(std::move(obj));
131   }
132
133   return arr;
134 }
135
136 static dynamic parseBser(Cursor& curs) {
137   switch ((BserType)curs.read<int8_t>()) {
138     case BserType::Int8:
139       return curs.read<int8_t>();
140     case BserType::Int16:
141       return curs.read<int16_t>();
142     case BserType::Int32:
143       return curs.read<int32_t>();
144     case BserType::Int64:
145       return curs.read<int64_t>();
146     case BserType::Real: {
147       double dval;
148       curs.pull((void*)&dval, sizeof(dval));
149       return dval;
150     }
151     case BserType::Null:
152       return nullptr;
153     case BserType::True:
154       return (bool)true;
155     case BserType::False:
156       return (bool)false;
157     case BserType::String:
158       return decodeString(curs);
159     case BserType::Array:
160       return decodeArray(curs);
161     case BserType::Object:
162       return decodeObject(curs);
163     case BserType::Template:
164       return decodeTemplate(curs);
165     case BserType::Skip:
166       throw std::runtime_error(
167           "Skip not valid at this location in the bser stream");
168     default:
169       throw std::runtime_error("invalid bser encoding");
170   }
171 }
172
173 static size_t decodeHeader(Cursor& curs) {
174   char header[sizeof(kMagic)];
175   curs.pull(header, sizeof(header));
176   if (memcmp(header, kMagic, sizeof(kMagic))) {
177     throw std::runtime_error("invalid BSER magic header");
178   }
179
180   auto enc = (BserType)curs.peekBytes().at(0);
181   size_t int_size;
182   switch (enc) {
183     case BserType::Int8:
184       int_size = 1;
185       break;
186     case BserType::Int16:
187       int_size = 2;
188       break;
189     case BserType::Int32:
190       int_size = 4;
191       break;
192     case BserType::Int64:
193       int_size = 8;
194       break;
195     default:
196       int_size = 0;
197   }
198
199   return int_size + 3 /* magic + int type */ + decodeInt(curs);
200 }
201
202 size_t decodePduLength(const folly::IOBuf* buf) {
203   Cursor curs(buf);
204   return decodeHeader(curs);
205 }
206
207 folly::dynamic parseBser(const IOBuf* buf) {
208   Cursor curs(buf);
209
210   decodeHeader(curs);
211   return parseBser(curs);
212 }
213
214 folly::dynamic parseBser(ByteRange str) {
215   auto buf = IOBuf::wrapBuffer(str.data(), str.size());
216   return parseBser(&*buf);
217 }
218
219 folly::dynamic parseBser(StringPiece str) {
220   return parseBser(ByteRange((uint8_t*)str.data(), str.size()));
221 }
222 } // namespace bser
223 } // namespace folly
224
225 /* vim:ts=2:sw=2:et:
226  */