feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,21 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
-fexceptions
-frtti
-std=c++20
-Wall
-Wpedantic
-DLOG_TAG=\"Fabric\")
file(GLOB react_render_mapbuffer_SRC CONFIGURE_DEPENDS *.cpp)
add_library(react_render_mapbuffer SHARED ${react_render_mapbuffer_SRC})
target_include_directories(react_render_mapbuffer PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_render_mapbuffer glog glog_init react_debug)

View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "MapBuffer.h"
using namespace facebook::react;
namespace facebook::react {
static inline int32_t bucketOffset(int32_t index) {
return sizeof(MapBuffer::Header) + sizeof(MapBuffer::Bucket) * index;
}
static inline int32_t valueOffset(int32_t bucketIndex) {
return bucketOffset(bucketIndex) + offsetof(MapBuffer::Bucket, data);
}
// TODO T83483191: Extend MapBuffer C++ implementation to support basic random
// access
MapBuffer::MapBuffer(std::vector<uint8_t> data) : bytes_(std::move(data)) {
auto header = reinterpret_cast<const Header*>(bytes_.data());
count_ = header->count;
if (header->bufferSize != bytes_.size()) {
LOG(ERROR) << "Error: Data size does not match, expected "
<< header->bufferSize << " found: " << bytes_.size();
abort();
}
}
int32_t MapBuffer::getKeyBucket(Key key) const {
int32_t lo = 0;
int32_t hi = count_ - 1;
while (lo <= hi) {
int32_t mid = (lo + hi) >> 1;
Key midVal =
*reinterpret_cast<const Key*>(bytes_.data() + bucketOffset(mid));
if (midVal < key) {
lo = mid + 1;
} else if (midVal > key) {
hi = mid - 1;
} else {
return mid;
}
}
return -1;
}
int32_t MapBuffer::getInt(Key key) const {
auto bucketIndex = getKeyBucket(key);
react_native_assert(bucketIndex != -1 && "Key not found in MapBuffer");
return *reinterpret_cast<const int32_t*>(
bytes_.data() + valueOffset(bucketIndex));
}
bool MapBuffer::getBool(Key key) const {
return getInt(key) != 0;
}
double MapBuffer::getDouble(Key key) const {
auto bucketIndex = getKeyBucket(key);
react_native_assert(bucketIndex != -1 && "Key not found in MapBuffer");
return *reinterpret_cast<const double*>(
bytes_.data() + valueOffset(bucketIndex));
}
int32_t MapBuffer::getDynamicDataOffset() const {
// The start of dynamic data can be calculated as the offset of the next
// key in the map
return bucketOffset(count_);
}
std::string MapBuffer::getString(Key key) const {
// TODO T83483191:Add checks to verify that offsets are under the boundaries
// of the map buffer
int32_t dynamicDataOffset = getDynamicDataOffset();
int32_t offset = getInt(key);
int32_t stringLength = *reinterpret_cast<const int32_t*>(
bytes_.data() + dynamicDataOffset + offset);
const uint8_t* stringPtr =
bytes_.data() + dynamicDataOffset + offset + sizeof(int);
return {stringPtr, stringPtr + stringLength};
}
MapBuffer MapBuffer::getMapBuffer(Key key) const {
// TODO T83483191: Add checks to verify that offsets are under the boundaries
// of the map buffer
int32_t dynamicDataOffset = getDynamicDataOffset();
int32_t offset = getInt(key);
int32_t mapBufferLength = *reinterpret_cast<const int32_t*>(
bytes_.data() + dynamicDataOffset + offset);
std::vector<uint8_t> value(mapBufferLength);
memcpy(
value.data(),
bytes_.data() + dynamicDataOffset + offset + sizeof(int32_t),
mapBufferLength);
return MapBuffer(std::move(value));
}
std::vector<MapBuffer> MapBuffer::getMapBufferList(MapBuffer::Key key) const {
std::vector<MapBuffer> mapBufferList;
int32_t dynamicDataOffset = getDynamicDataOffset();
int32_t offset = getInt(key);
int32_t mapBufferListLength = *reinterpret_cast<const int32_t*>(
bytes_.data() + dynamicDataOffset + offset);
offset = offset + sizeof(uint32_t);
int32_t curLen = 0;
while (curLen < mapBufferListLength) {
int32_t mapBufferLength = *reinterpret_cast<const int32_t*>(
bytes_.data() + dynamicDataOffset + offset + curLen);
curLen = curLen + sizeof(uint32_t);
std::vector<uint8_t> value(mapBufferLength);
memcpy(
value.data(),
bytes_.data() + dynamicDataOffset + offset + curLen,
mapBufferLength);
mapBufferList.emplace_back(std::move(value));
curLen = curLen + mapBufferLength;
}
return mapBufferList;
}
size_t MapBuffer::size() const {
return bytes_.size();
}
const uint8_t* MapBuffer::data() const {
return bytes_.data();
}
uint16_t MapBuffer::count() const {
return count_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,153 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/debug/react_native_assert.h>
#include <glog/logging.h>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <string>
#include <vector>
namespace facebook::react {
class JReadableMapBuffer;
// clang-format off
/**
* MapBuffer is an optimized sparse array format for transferring props-like
* objects between C++ and other VMs. The implementation of this map is optimized to:
* - be compact to optimize space when sparse (sparse is the common case).
* - be accessible through JNI with zero/minimal copying via ByteBuffer.
* - have excellent C++ single-write and many-read performance by maximizing
* CPU cache performance through compactness, data locality, and fixed offsets
* where possible.
* - be optimized for iteration and intersection against other maps, but with
* reasonably good random access as well.
* - work recursively for nested maps/arrays.
* - support dynamic types that map to JSON.
* - don't require mutability/copy - single-write on creation and move semantics.
* - have minimal APK size and build time impact.
*
* MapBuffer data is stored in a continuous chunk of memory (bytes_ field below) with the following layout:
*
* ┌─────────────────────Header──────────────────────┐
* │ 10 bytes │
* ├─Alignment─┬─Item count─┬──────Buffer size───────┤
* │ 2 bytes │ 2 bytes │ 4 bytes │
* └───────────┴────────────┴────────────────────────┘
* ┌────────────────────────────────────────────────────────────────────────────────────────┐
* │ Buckets (one per item in the map) │
* │ │
* ├───────────────────────────Bucket───────────────────────────┬───Bucket────┬─────────────┤
* │ 12 bytes │ 12 bytes │ │
* ├───Key───┬──Type───┬──────Value (primitive or offset)───────┤ ... │ ... │
* │ 2 bytes │ 2 bytes │ 8 bytes │ │ │
* └─────────┴─────────┴────────────────────────────────────────┴─────────────┴─────────────┘
* ┌────────────────────────────────────────────────────────────────────────────────────────┐
* │ Dynamic data │
* │ │
* │ Free-form data for complex objects (e.g. strings or nested MapBuffers). │
* │ When dynamic data is serialized with some object, bucket value contains an offset of │
* │ associated byte in the array. The format of the data is not restricted, but common │
* │ practice is to use [length | bytes]. │
* └────────────────────────────────────────────────────────────────────────────────────────┘
*/
// clang-format on
class MapBuffer {
public:
using Key = uint16_t;
// The first value in the buffer, used to check correct encoding/endianness on
// JVM side.
constexpr static uint16_t HEADER_ALIGNMENT = 0xFE;
struct Header {
uint16_t alignment = HEADER_ALIGNMENT; // alignment of serialization
uint16_t count; // amount of items in the map
uint32_t bufferSize; // Amount of bytes used to store the map in memory
};
#pragma pack(push, 1)
struct Bucket {
Key key;
uint16_t type;
uint64_t data;
Bucket(Key key, uint16_t type, uint64_t data)
: key(key), type(type), data(data) {}
};
#pragma pack(pop)
static_assert(sizeof(Header) == 8, "MapBuffer header size is incorrect.");
static_assert(sizeof(Bucket) == 12, "MapBuffer bucket size is incorrect.");
/**
* Data types available for serialization in MapBuffer
* Keep in sync with `DataType` enum in `JReadableMapBuffer.java`, which
* expects the same values after reading them through JNI.
*/
enum DataType : uint16_t {
Boolean = 0,
Int = 1,
Double = 2,
String = 3,
Map = 4,
};
explicit MapBuffer(std::vector<uint8_t> data);
MapBuffer(const MapBuffer& buffer) = delete;
MapBuffer& operator=(const MapBuffer& other) = delete;
MapBuffer(MapBuffer&& buffer) = default;
MapBuffer& operator=(MapBuffer&& other) = default;
int32_t getInt(MapBuffer::Key key) const;
bool getBool(MapBuffer::Key key) const;
double getDouble(MapBuffer::Key key) const;
std::string getString(MapBuffer::Key key) const;
// TODO T83483191: review this declaration
MapBuffer getMapBuffer(MapBuffer::Key key) const;
std::vector<MapBuffer> getMapBufferList(MapBuffer::Key key) const;
size_t size() const;
const uint8_t* data() const;
uint16_t count() const;
private:
// Buffer and its size
std::vector<uint8_t> bytes_;
// amount of items in the MapBuffer
uint16_t count_ = 0;
// returns the relative offset of the first byte of dynamic data
int32_t getDynamicDataOffset() const;
int32_t getKeyBucket(MapBuffer::Key key) const;
friend JReadableMapBuffer;
};
} // namespace facebook::react

View File

@@ -0,0 +1,180 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "MapBufferBuilder.h"
#include <algorithm>
using namespace facebook::react;
namespace facebook::react {
constexpr uint32_t INT_SIZE = sizeof(uint32_t);
constexpr uint32_t DOUBLE_SIZE = sizeof(double);
constexpr uint32_t MAX_BUCKET_VALUE_SIZE = sizeof(uint64_t);
MapBuffer MapBufferBuilder::EMPTY() {
return MapBufferBuilder(0).build();
}
MapBufferBuilder::MapBufferBuilder(uint32_t initialSize) {
buckets_.reserve(initialSize);
header_.count = 0;
header_.bufferSize = 0;
}
void MapBufferBuilder::storeKeyValue(
MapBuffer::Key key,
MapBuffer::DataType type,
const uint8_t* value,
uint32_t valueSize) {
if (valueSize > MAX_BUCKET_VALUE_SIZE) {
LOG(ERROR) << "Error: size of value must be <= MAX_VALUE_SIZE. ValueSize: "
<< valueSize;
abort();
}
uint64_t data = 0;
auto* dataPtr = reinterpret_cast<uint8_t*>(&data);
memcpy(dataPtr, value, valueSize);
buckets_.emplace_back(key, static_cast<uint16_t>(type), data);
header_.count++;
if (lastKey_ > key) {
needsSort_ = true;
}
lastKey_ = key;
}
void MapBufferBuilder::putBool(MapBuffer::Key key, bool value) {
int intValue = (int)value;
storeKeyValue(
key,
MapBuffer::DataType::Boolean,
reinterpret_cast<const uint8_t*>(&intValue),
INT_SIZE);
}
void MapBufferBuilder::putDouble(MapBuffer::Key key, double value) {
storeKeyValue(
key,
MapBuffer::DataType::Double,
reinterpret_cast<const uint8_t*>(&value),
DOUBLE_SIZE);
}
void MapBufferBuilder::putInt(MapBuffer::Key key, int32_t value) {
storeKeyValue(
key,
MapBuffer::DataType::Int,
reinterpret_cast<const uint8_t*>(&value),
INT_SIZE);
}
void MapBufferBuilder::putString(MapBuffer::Key key, const std::string& value) {
auto strSize = value.size();
const char* strData = value.data();
// format [length of string (int)] + [Array of Characters in the string]
auto offset = dynamicData_.size();
dynamicData_.resize(offset + INT_SIZE + strSize, 0);
memcpy(dynamicData_.data() + offset, &strSize, INT_SIZE);
memcpy(dynamicData_.data() + offset + INT_SIZE, strData, strSize);
// Store Key and pointer to the string
storeKeyValue(
key,
MapBuffer::DataType::String,
reinterpret_cast<const uint8_t*>(&offset),
INT_SIZE);
}
void MapBufferBuilder::putMapBuffer(MapBuffer::Key key, const MapBuffer& map) {
auto mapBufferSize = map.size();
auto offset = dynamicData_.size();
// format [length of buffer (int)] + [bytes of MapBuffer]
dynamicData_.resize(offset + INT_SIZE + mapBufferSize, 0);
memcpy(dynamicData_.data() + offset, &mapBufferSize, INT_SIZE);
// Copy the content of the map into dynamicData_
memcpy(dynamicData_.data() + offset + INT_SIZE, map.data(), mapBufferSize);
// Store Key and pointer to the string
storeKeyValue(
key,
MapBuffer::DataType::Map,
reinterpret_cast<const uint8_t*>(&offset),
INT_SIZE);
}
void MapBufferBuilder::putMapBufferList(
MapBuffer::Key key,
const std::vector<MapBuffer>& mapBufferList) {
auto offset = static_cast<int32_t>(dynamicData_.size());
int32_t dataSize = 0;
for (const MapBuffer& mapBuffer : mapBufferList) {
dataSize = dataSize + INT_SIZE + static_cast<int32_t>(mapBuffer.size());
}
dynamicData_.resize(offset + INT_SIZE, 0);
memcpy(dynamicData_.data() + offset, &dataSize, INT_SIZE);
for (const MapBuffer& mapBuffer : mapBufferList) {
auto mapBufferSize = static_cast<int32_t>(mapBuffer.size());
auto dynamicDataSize = static_cast<int32_t>(dynamicData_.size());
dynamicData_.resize(dynamicDataSize + INT_SIZE + mapBufferSize, 0);
// format [length of buffer (int)] + [bytes of MapBuffer]
memcpy(dynamicData_.data() + dynamicDataSize, &mapBufferSize, INT_SIZE);
// Copy the content of the map into dynamicData_
memcpy(
dynamicData_.data() + dynamicDataSize + INT_SIZE,
mapBuffer.data(),
mapBufferSize);
}
// Store Key and pointer to the string
storeKeyValue(
key,
MapBuffer::DataType::Map,
reinterpret_cast<const uint8_t*>(&offset),
INT_SIZE);
}
static inline bool compareBuckets(
const MapBuffer::Bucket& a,
const MapBuffer::Bucket& b) {
return a.key < b.key;
}
MapBuffer MapBufferBuilder::build() {
// Create buffer: [header] + [key, values] + [dynamic data]
auto bucketSize = buckets_.size() * sizeof(MapBuffer::Bucket);
auto headerSize = sizeof(MapBuffer::Header);
auto bufferSize = headerSize + bucketSize + dynamicData_.size();
header_.bufferSize = static_cast<uint32_t>(bufferSize);
if (needsSort_) {
std::sort(buckets_.begin(), buckets_.end(), compareBuckets);
}
// TODO(T83483191): add pass to check for duplicates
std::vector<uint8_t> buffer(bufferSize);
memcpy(buffer.data(), &header_, headerSize);
memcpy(buffer.data() + headerSize, buckets_.data(), bucketSize);
memcpy(
buffer.data() + headerSize + bucketSize,
dynamicData_.data(),
dynamicData_.size());
return MapBuffer(std::move(buffer));
}
} // namespace facebook::react

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/debug/react_native_assert.h>
#include <vector>
#include "MapBuffer.h"
namespace facebook::react {
// Default reserved size for buckets_ vector
constexpr uint32_t INITIAL_BUCKETS_SIZE = 10;
/**
* MapBufferBuilder is a builder class for MapBuffer
*/
class MapBufferBuilder {
public:
MapBufferBuilder(uint32_t initialSize = INITIAL_BUCKETS_SIZE);
static MapBuffer EMPTY();
void putInt(MapBuffer::Key key, int32_t value);
// TODO: Support 64 bit integers
void putBool(MapBuffer::Key key, bool value);
void putDouble(MapBuffer::Key key, double value);
void putString(MapBuffer::Key key, const std::string& value);
void putMapBuffer(MapBuffer::Key key, const MapBuffer& map);
void putMapBufferList(
MapBuffer::Key key,
const std::vector<MapBuffer>& mapBufferList);
MapBuffer build();
private:
MapBuffer::Header header_;
std::vector<MapBuffer::Bucket> buckets_{};
std::vector<uint8_t> dynamicData_{};
uint16_t lastKey_{0};
bool needsSort_{false};
void storeKeyValue(
MapBuffer::Key key,
MapBuffer::DataType type,
const uint8_t* value,
uint32_t valueSize);
};
} // namespace facebook::react

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <vector>
#include <gtest/gtest.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
using namespace facebook::react;
TEST(MapBufferTest, testSimpleIntMap) {
auto builder = MapBufferBuilder();
builder.putInt(0, 1234);
builder.putInt(1, 4321);
auto map = builder.build();
EXPECT_EQ(map.count(), 2);
EXPECT_EQ(map.getInt(0), 1234);
EXPECT_EQ(map.getInt(1), 4321);
}
TEST(MapBufferTest, testMapBufferExtension) {
// 26 = 2 buckets: 2*10 + 6 sizeof(header)
int initialSize = 26;
auto buffer = MapBufferBuilder(initialSize);
buffer.putInt(0, 1234);
buffer.putInt(1, 4321);
buffer.putInt(2, 2121);
buffer.putInt(3, 1212);
auto map = buffer.build();
EXPECT_EQ(map.count(), 4);
EXPECT_EQ(map.getInt(0), 1234);
EXPECT_EQ(map.getInt(1), 4321);
EXPECT_EQ(map.getInt(2), 2121);
EXPECT_EQ(map.getInt(3), 1212);
}
TEST(MapBufferTest, testBoolEntries) {
auto buffer = MapBufferBuilder();
buffer.putBool(0, true);
buffer.putBool(1, false);
auto map = buffer.build();
EXPECT_EQ(map.count(), 2);
EXPECT_EQ(map.getBool(0), true);
EXPECT_EQ(map.getBool(1), false);
}
TEST(MapBufferTest, testDoubleEntries) {
auto buffer = MapBufferBuilder();
buffer.putDouble(0, 123.4);
buffer.putDouble(1, 432.1);
auto map = buffer.build();
EXPECT_EQ(map.count(), 2);
EXPECT_EQ(map.getDouble(0), 123.4);
EXPECT_EQ(map.getDouble(1), 432.1);
}
TEST(MapBufferTest, testStringEntries) {
auto builder = MapBufferBuilder();
builder.putString(0, "This is a test");
auto map = builder.build();
EXPECT_EQ(map.getString(0), "This is a test");
}
TEST(MapBufferTest, testUTFStringEntry) {
auto builder = MapBufferBuilder();
builder.putString(0, "Let's count: 的, 一, 是");
auto map = builder.build();
EXPECT_EQ(map.getString(0), "Let's count: 的, 一, 是");
}
TEST(MapBufferTest, testEmojiStringEntry) {
auto builder = MapBufferBuilder();
builder.putString(
0, "Let's count: 1⃣, 2⃣, 3⃣, 🤦🏿‍♀️");
auto map = builder.build();
EXPECT_EQ(
map.getString(0),
"Let's count: 1⃣, 2⃣, 3⃣, 🤦🏿‍♀️");
}
TEST(MapBufferTest, testUTFStringEntries) {
auto builder = MapBufferBuilder();
builder.putString(0, "Let's count: 的, 一, 是");
builder.putString(1, "This is a test");
auto map = builder.build();
EXPECT_EQ(map.getString(0), "Let's count: 的, 一, 是");
EXPECT_EQ(map.getString(1), "This is a test");
}
TEST(MapBufferTest, testEmptyMap) {
auto builder = MapBufferBuilder();
auto map = builder.build();
EXPECT_EQ(map.count(), 0);
}
TEST(MapBufferTest, testEmptyMapConstant) {
auto map = MapBufferBuilder::EMPTY();
EXPECT_EQ(map.count(), 0);
}
TEST(MapBufferTest, testMapEntries) {
auto builder = MapBufferBuilder();
builder.putString(0, "This is a test");
builder.putInt(1, 1234);
auto map = builder.build();
EXPECT_EQ(map.count(), 2);
EXPECT_EQ(map.getString(0), "This is a test");
EXPECT_EQ(map.getInt(1), 1234);
auto builder2 = MapBufferBuilder();
builder2.putInt(0, 4321);
builder2.putMapBuffer(1, map);
auto map2 = builder2.build();
EXPECT_EQ(map2.count(), 2);
EXPECT_EQ(map2.getInt(0), 4321);
MapBuffer readMap2 = map2.getMapBuffer(1);
EXPECT_EQ(readMap2.count(), 2);
EXPECT_EQ(readMap2.getString(0), "This is a test");
EXPECT_EQ(readMap2.getInt(1), 1234);
}
TEST(MapBufferTest, testMapListEntries) {
std::vector<MapBuffer> mapBufferList;
auto builder = MapBufferBuilder();
builder.putString(0, "This is a test");
builder.putInt(1, 1234);
mapBufferList.push_back(builder.build());
auto builder2 = MapBufferBuilder();
builder2.putInt(2, 4321);
builder2.putDouble(3, 908.1);
mapBufferList.push_back(builder2.build());
auto builder3 = MapBufferBuilder();
builder3.putMapBufferList(5, mapBufferList);
auto map = builder3.build();
std::vector<MapBuffer> mapBufferList2 = map.getMapBufferList(5);
EXPECT_EQ(mapBufferList2.size(), 2);
EXPECT_EQ(mapBufferList2[0].getString(0), "This is a test");
EXPECT_EQ(mapBufferList2[0].getInt(1), 1234);
EXPECT_EQ(mapBufferList2[1].getDouble(3), 908.1);
}
TEST(MapBufferTest, testMapRandomAccess) {
auto builder = MapBufferBuilder();
builder.putInt(1234, 4321);
builder.putString(0, "This is a test");
builder.putDouble(8, 908.1);
builder.putString(65535, "Let's count: 的, 一, 是");
auto map = builder.build();
EXPECT_EQ(map.count(), 4);
EXPECT_EQ(map.getString(0), "This is a test");
EXPECT_EQ(map.getDouble(8), 908.1);
EXPECT_EQ(map.getInt(1234), 4321);
EXPECT_EQ(map.getString(65535), "Let's count: 的, 一, 是");
}