Skip to content

Commit 910c277

Browse files
markchandlera-maurice
authored andcommitted
Firebase cpp: Added small string support to Firebase Variant
Reduced number of new/delete for Variant copies of same type Ensure sort order of Variant in map is consistent PiperOrigin-RevId: 273401937
1 parent ce8f3d3 commit 910c277

File tree

6 files changed

+181
-33
lines changed

6 files changed

+181
-33
lines changed

app/src/include/firebase/variant.h

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class Variant {
6969
/// Variant::FromMutableBlob() to create a Variant of this type, and copy
7070
/// binary data from an existing source.
7171
kTypeMutableBlob,
72+
/// A c string stored in the Variant internal data blob as opposed to be
73+
/// newed as a std::string. Max size is 16 bytes on x64 and 8 bytes on x86.
74+
kTypeSmallString,
75+
/// Not a valid type. Used to get the total number of Variant types.
76+
kMaxTypeValue,
7277
};
7378

7479
// <SWIG>
@@ -455,14 +460,21 @@ class Variant {
455460
/// @return True if the Variant's type is MutableString, false otherwise.
456461
bool is_mutable_string() const { return type() == kTypeMutableString; }
457462

463+
/// @brief Get whether this Variant contains a small string.
464+
///
465+
/// @return True if the Variant's type is SmallString, false otherwise.
466+
bool is_small_string() const { return type() == kTypeSmallString; }
467+
458468
/// @brief Get whether this Variant contains a string.
459469
///
460470
/// @return True if the Variant's type is either StaticString or
461-
/// MutableString; false otherwise.
471+
/// MutableString or SmallString; false otherwise.
462472
///
463473
/// @note No matter which type of string the Variant contains, you can read
464474
/// its value via string_value().
465-
bool is_string() const { return is_static_string() || is_mutable_string(); }
475+
bool is_string() const {
476+
return is_static_string() || is_mutable_string() || is_small_string();
477+
}
466478

467479
/// @brief Get whether this Variant contains a static blob.
468480
///
@@ -554,9 +566,9 @@ class Variant {
554566
///
555567
/// @note If the Variant is not one of the two String types, this will assert.
556568
std::string& mutable_string() {
557-
if (type_ == kTypeStaticString) {
558-
// Automatically promote a static string to a mutable string.
559-
set_mutable_string(string_value());
569+
if (type_ == kTypeStaticString || type_ == kTypeSmallString) {
570+
// Automatically promote a static or small string to a mutable string.
571+
set_mutable_string(string_value(), false);
560572
}
561573
assert_is_type(kTypeMutableString);
562574
return *value_.mutable_string_value;
@@ -674,8 +686,10 @@ class Variant {
674686
assert_is_string();
675687
if (type_ == kTypeMutableString)
676688
return value_.mutable_string_value->c_str();
677-
else // type_ == kTypeStaticString
689+
else if (type_ == kTypeStaticString)
678690
return value_.static_string_value;
691+
else // if (type_ == kTypeSmallString)
692+
return value_.small_string;
679693
}
680694

681695
/// @brief Const accessor for a Variant containing a mutable string only.
@@ -770,7 +784,15 @@ class Variant {
770784
///
771785
/// @param[in] value A pointer to a null-terminated string, which will be
772786
/// copied into to the Variant.
773-
void set_string_value(char* value) { set_mutable_string(value); }
787+
void set_string_value(char* value) {
788+
size_t len = strlen(value);
789+
if (len < kMaxSmallStringSize) {
790+
Clear(kTypeSmallString);
791+
strncpy(value_.small_string, value, len + 1);
792+
} else {
793+
set_mutable_string(std::string(value, len));
794+
}
795+
}
774796

775797
/// @brief Sets the Variant to a mutable string.
776798
///
@@ -781,12 +803,22 @@ class Variant {
781803

782804
/// @brief Sets the Variant to a copy of the given string.
783805
///
784-
/// The Variant's type will be set to MutableString.
806+
/// The Variant's type will be set to SmallString if the size of the string is
807+
/// less than kMaxSmallStringSize (8 bytes on x86, 16 bytes on x64) or
808+
/// otherwise set to MutableString.
785809
///
786810
/// @param[in] value The string to use for the Variant.
787-
void set_mutable_string(const std::string& value) {
788-
Clear(kTypeMutableString);
789-
*value_.mutable_string_value = value;
811+
/// @param[in] use_small_string Check to see if the input string should be
812+
/// treated as a small string or left as a mutable string
813+
void set_mutable_string(const std::string& value,
814+
bool use_small_string = true) {
815+
if (value.size() < kMaxSmallStringSize && use_small_string) {
816+
Clear(kTypeSmallString);
817+
strncpy(value_.small_string, value.data(), value.size() + 1);
818+
} else {
819+
Clear(kTypeMutableString);
820+
*value_.mutable_string_value = value;
821+
}
790822
}
791823

792824
/// @brief Sets the Variant to a copy of the given binary data.
@@ -1047,7 +1079,10 @@ class Variant {
10471079
const uint8_t* ptr;
10481080
size_t size;
10491081
} blob_value;
1082+
char small_string[sizeof(blob_value)];
10501083
} value_;
1084+
1085+
static const size_t kMaxSmallStringSize = sizeof(Value::small_string);
10511086
};
10521087

10531088
template <>

app/src/util_ios.mm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ id VariantToId(const Variant& variant) {
213213
return [NSNumber numberWithBool:variant.bool_value()];
214214
}
215215
case Variant::kTypeStaticString:
216-
case Variant::kTypeMutableString: {
216+
case Variant::kTypeMutableString:
217+
case Variant::kTypeSmallString: {
217218
return @(variant.string_value());
218219
}
219220
case Variant::kTypeVector: {

app/src/variant.cc

Lines changed: 111 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ Variant& Variant::operator=(const Variant& other) {
5454
set_mutable_string(other.mutable_string());
5555
break;
5656
}
57+
case kTypeSmallString: {
58+
strcpy(value_.small_string, other.value_.small_string); // NOLINT
59+
break;
60+
}
5761
case kTypeVector: {
5862
set_vector(other.vector());
5963
break;
@@ -72,6 +76,9 @@ Variant& Variant::operator=(const Variant& other) {
7276
other.value_.blob_value.size);
7377
break;
7478
}
79+
case kMaxTypeValue:
80+
FIREBASE_ASSERT(false); // Should never hit this
81+
break;
7582
}
7683
}
7784
return *this;
@@ -110,6 +117,12 @@ Variant& Variant::operator=(Variant&& other) {
110117
other.value_.mutable_string_value = nullptr;
111118
break;
112119
}
120+
case kTypeSmallString: {
121+
memcpy(value_.small_string, other.value_.small_string,
122+
kMaxSmallStringSize);
123+
other.value_.small_string[0] = '\0';
124+
break;
125+
}
113126
case kTypeVector: {
114127
value_.vector_value = other.value_.vector_value;
115128
other.value_.vector_value = nullptr;
@@ -132,6 +145,9 @@ Variant& Variant::operator=(Variant&& other) {
132145
other.value_.blob_value.size = 0;
133146
break;
134147
}
148+
case kMaxTypeValue:
149+
FIREBASE_ASSERT(false); // Should never hit this
150+
break;
135151
}
136152
}
137153
return *this;
@@ -156,6 +172,7 @@ bool Variant::operator==(const Variant& other) const {
156172
return bool_value() == other.bool_value();
157173
case kTypeMutableString:
158174
case kTypeStaticString:
175+
case kTypeSmallString:
159176
// string == performs string comparison
160177
return strcmp(string_value(), other.string_value()) == 0;
161178
case kTypeVector:
@@ -172,16 +189,45 @@ bool Variant::operator==(const Variant& other) const {
172189
((is_static_blob() && other.is_static_blob() &&
173190
blob_data() == other.blob_data()) ||
174191
memcmp(blob_data(), other.blob_data(), blob_size()) == 0);
192+
case kMaxTypeValue:
193+
FIREBASE_ASSERT(false); // Should never hit this
194+
break;
175195
}
176196
return false; // Should never reach this.
177197
}
178198

179199
bool Variant::operator<(const Variant& other) const {
200+
Type left_type = type();
201+
Type right_type = other.type();
202+
203+
// If we are any string type set type to static string as we care about string
204+
// value not type of string.
205+
if (is_string()) {
206+
left_type = kTypeStaticString;
207+
}
208+
209+
// If other is any string type set type to static string as we care about
210+
// string value not type of string.
211+
if (other.is_string()) {
212+
right_type = kTypeStaticString;
213+
}
214+
215+
// If we are any blob type set type to static blob as we care about blob value
216+
// not type of blob.
217+
if (is_blob()) {
218+
left_type = kTypeStaticBlob;
219+
}
220+
221+
// If other is any blob type set type to static blob as we care about blob
222+
// value not type of blob.
223+
if (other.is_blob()) {
224+
right_type = kTypeStaticBlob;
225+
}
226+
180227
// If the types don't match (except count both string types as matching, and
181228
// both blob types as matching), compare the types.
182-
if (type() != other.type() && !(is_string() && other.is_string()) &&
183-
!(is_blob() && other.is_blob()))
184-
return static_cast<int>(type()) < static_cast<int>(other.type());
229+
if (left_type != right_type)
230+
return static_cast<int>(left_type) < static_cast<int>(right_type);
185231
// Type is now equal (or both strings, or both blobs).
186232
switch (type_) {
187233
case kTypeNull: {
@@ -198,6 +244,7 @@ bool Variant::operator<(const Variant& other) const {
198244
}
199245
case kTypeMutableString:
200246
case kTypeStaticString:
247+
case kTypeSmallString:
201248
return strcmp(string_value(), other.string_value()) < 0;
202249
case kTypeVector: {
203250
auto i = vector().begin();
@@ -229,6 +276,9 @@ bool Variant::operator<(const Variant& other) const {
229276
return blob_size() == other.blob_size()
230277
? memcmp(blob_data(), other.blob_data(), blob_size()) < 0
231278
: blob_size() < other.blob_size();
279+
case kMaxTypeValue:
280+
FIREBASE_ASSERT(false); // Should never hit this
281+
break;
232282
}
233283
return false; // Should never reach this.
234284
}
@@ -255,18 +305,35 @@ void Variant::Clear(Type new_type) {
255305
break;
256306
}
257307
case kTypeMutableString: {
258-
delete value_.mutable_string_value;
259-
value_.mutable_string_value = nullptr;
308+
if (new_type != kTypeMutableString
309+
|| value_.mutable_string_value == nullptr) {
310+
delete value_.mutable_string_value;
311+
value_.mutable_string_value = nullptr;
312+
} else {
313+
value_.mutable_string_value->clear();
314+
}
315+
break;
316+
}
317+
case kTypeSmallString: {
318+
value_.small_string[0] = '\0';
260319
break;
261320
}
262321
case kTypeVector: {
263-
delete value_.vector_value;
264-
value_.vector_value = nullptr;
322+
if (new_type != kTypeVector || value_.vector_value == nullptr) {
323+
delete value_.vector_value;
324+
value_.vector_value = nullptr;
325+
} else {
326+
value_.vector_value->clear();
327+
}
265328
break;
266329
}
267330
case kTypeMap: {
268-
delete value_.map_value;
269-
value_.map_value = nullptr;
331+
if (new_type != kTypeMap || value_.map_value == nullptr) {
332+
delete value_.map_value;
333+
value_.map_value = nullptr;
334+
} else {
335+
value_.map_value->clear();
336+
}
270337
break;
271338
}
272339
case kTypeStaticBlob: {
@@ -279,7 +346,12 @@ void Variant::Clear(Type new_type) {
279346
delete[] prev_data;
280347
break;
281348
}
349+
case kMaxTypeValue:
350+
FIREBASE_ASSERT(false); // Should never hit this
351+
break;
282352
}
353+
354+
Type old_type = type_;
283355
type_ = new_type;
284356
switch (type_) {
285357
case kTypeNull: {
@@ -302,15 +374,26 @@ void Variant::Clear(Type new_type) {
302374
break;
303375
}
304376
case kTypeMutableString: {
305-
value_.mutable_string_value = new std::string();
377+
if (old_type != kTypeMutableString ||
378+
value_.mutable_string_value == nullptr) {
379+
value_.mutable_string_value = new std::string();
380+
}
381+
break;
382+
}
383+
case kTypeSmallString: {
384+
value_.small_string[0] = '\0';
306385
break;
307386
}
308387
case kTypeVector: {
309-
value_.vector_value = new std::vector<Variant>(0);
388+
if (old_type != kTypeVector || value_.vector_value == nullptr) {
389+
value_.vector_value = new std::vector<Variant>(0);
390+
}
310391
break;
311392
}
312393
case kTypeMap: {
313-
value_.map_value = new std::map<Variant, Variant>();
394+
if (old_type != kTypeMap || value_.map_value == nullptr) {
395+
value_.map_value = new std::map<Variant, Variant>();
396+
}
314397
break;
315398
}
316399
case kTypeStaticBlob: {
@@ -321,17 +404,24 @@ void Variant::Clear(Type new_type) {
321404
set_blob_pointer(nullptr, 0);
322405
break;
323406
}
407+
case kMaxTypeValue:
408+
FIREBASE_ASSERT(false); // Should never hit this
409+
break;
324410
}
325411
}
326412

327413
const char* const Variant::kTypeNames[] = {
328414
// In case you want to iterate through these for some reason.
329-
"Null", "Int64", "Double", "Bool",
330-
"StaticString", "MutableString", "Vector", "Map",
331-
"StaticBlob", "MutableBlob", nullptr,
415+
"Null", "Int64", "Double", "Bool",
416+
"StaticString", "MutableString", "Vector", "Map",
417+
"StaticBlob", "MutableBlob", "SmallString", nullptr,
332418
};
333419

334420
void Variant::assert_is_type(Variant::Type type) const {
421+
static_assert(FIREBASE_ARRAYSIZE(Variant::kTypeNames) ==
422+
Variant::kMaxTypeValue + 1,
423+
"Type Enum should match kTypeNames");
424+
335425
FIREBASE_ASSERT_MESSAGE(
336426
this->type() == type,
337427
"Expected Variant to be of type %s, but it was of type %s.",
@@ -394,7 +484,8 @@ Variant Variant::AsString() const {
394484
return bool_value() ? Variant("true") : Variant("false");
395485
}
396486
case kTypeMutableString:
397-
case kTypeStaticString: {
487+
case kTypeStaticString:
488+
case kTypeSmallString: {
398489
return *this;
399490
}
400491
default: {
@@ -415,7 +506,8 @@ Variant Variant::AsInt64() const {
415506
return bool_value() ? Variant::One() : Variant::Zero();
416507
}
417508
case kTypeMutableString:
418-
case kTypeStaticString: {
509+
case kTypeStaticString:
510+
case kTypeSmallString: {
419511
return Variant::FromInt64(strtol(string_value(), nullptr, 10)); // NOLINT
420512
}
421513
default: {
@@ -436,7 +528,8 @@ Variant Variant::AsDouble() const {
436528
return bool_value() ? Variant::OnePointZero() : Variant::ZeroPointZero();
437529
}
438530
case kTypeMutableString:
439-
case kTypeStaticString: {
531+
case kTypeStaticString:
532+
case kTypeSmallString: {
440533
return Variant::FromDouble(strtod(string_value(), nullptr));
441534
}
442535
default: {

0 commit comments

Comments
 (0)