diff --git a/AK/JsonArray.cpp b/AK/JsonArray.cpp new file mode 100644 index 00000000000..49188889e93 --- /dev/null +++ b/AK/JsonArray.cpp @@ -0,0 +1,15 @@ +#include +#include + +String JsonArray::to_string() const +{ + StringBuilder builder; + builder.append('['); + for (int i = 0; i < m_values.size(); ++i) { + builder.append(m_values[i].to_string()); + if (i != size() - 1) + builder.append(','); + } + builder.append(']'); + return builder.to_string(); +} diff --git a/AK/JsonArray.h b/AK/JsonArray.h new file mode 100644 index 00000000000..38eaad09c6f --- /dev/null +++ b/AK/JsonArray.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +class JsonArray { +public: + JsonArray() {} + ~JsonArray() {} + + int size() const { return m_values.size(); } + bool is_empty() const { return m_values.is_empty(); } + + const JsonValue& at(int index) const { return m_values.at(index); } + const JsonValue& operator[](int index) const { return at(index); } + + void clear() { m_values.clear(); } + void append(const JsonValue& value) { m_values.append(value); } + + String to_string() const; + +private: + Vector m_values; +}; diff --git a/AK/JsonObject.cpp b/AK/JsonObject.cpp new file mode 100644 index 00000000000..11f01bd235f --- /dev/null +++ b/AK/JsonObject.cpp @@ -0,0 +1,21 @@ +#include +#include + +String JsonObject::to_string() const +{ + StringBuilder builder; + int index = 0; + builder.append('{'); + for_each_member([&] (auto& key, auto& value) { + builder.append('"'); + builder.append(key); + builder.append('"'); + builder.append(':'); + builder.append(value.to_string()); + if (index != size() - 1) + builder.append(','); + ++index; + }); + builder.append('}'); + return builder.to_string(); +} diff --git a/AK/JsonObject.h b/AK/JsonObject.h new file mode 100644 index 00000000000..ab01ae6675f --- /dev/null +++ b/AK/JsonObject.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +class JsonObject { +public: + JsonObject() { } + ~JsonObject() { } + + JsonObject(const JsonObject& other) + { + for (auto& it : other.m_members) + m_members.set(it.key, it.value); + } + + int size() const { return m_members.size(); } + bool is_empty() const { return m_members.is_empty(); } + + JsonValue get(const String& key) const + { + auto it = m_members.find(key); + if (it == m_members.end()) + return JsonValue(JsonValue::Type::Undefined); + return (*it).value; + } + + void set(const String& key, const JsonValue& value) + { + m_members.set(key, value); + } + + template + void for_each_member(Callback callback) const + { + for (auto& it : m_members) + callback(it.key, it.value); + } + + String to_string() const; + +private: + HashMap m_members; +}; diff --git a/AK/JsonValue.cpp b/AK/JsonValue.cpp new file mode 100644 index 00000000000..4ed88bb16d2 --- /dev/null +++ b/AK/JsonValue.cpp @@ -0,0 +1,140 @@ +#include +#include +#include + +JsonValue::JsonValue(Type type) + : m_type(type) +{ +} + +JsonValue::JsonValue(const JsonValue& other) +{ + copy_from(other); +} + +JsonValue& JsonValue::operator=(const JsonValue& other) +{ + if (this != &other) { + clear(); + copy_from(other); + } + return *this; +} + +void JsonValue::copy_from(const JsonValue& other) +{ + m_type = other.m_type; + switch (m_type) { + case Type::String: + m_value.as_string = other.m_value.as_string; + AK::retain_if_not_null(m_value.as_string); + break; + case Type::Object: + m_value.as_object = new JsonObject(*other.m_value.as_object); + break; + case Type::Array: + m_value.as_array = new JsonArray(*other.m_value.as_array); + break; + default: + m_value.as_string = other.m_value.as_string; + break; + } +} + +JsonValue::JsonValue(JsonValue&& other) +{ + m_type = exchange(other.m_type, Type::Undefined); + m_value.as_string = exchange(other.m_value.as_string, nullptr); +} + +JsonValue& JsonValue::operator=(JsonValue&& other) +{ + if (this != &other) { + m_type = exchange(other.m_type, Type::Undefined); + m_value.as_string = exchange(other.m_value.as_string, nullptr); + } + return *this; +} + +JsonValue::JsonValue(int value) + : m_type(Type::Int) +{ + m_value.as_int = value; +} + +JsonValue::JsonValue(double value) + : m_type(Type::Double) +{ + m_value.as_double = value; +} + +JsonValue::JsonValue(bool value) + : m_type(Type::Bool) +{ + m_value.as_bool = value; +} + +JsonValue::JsonValue(const String& value) +{ + if (value.is_null()) { + m_type = Type::Null; + } else { + m_type = Type::String; + m_value.as_string = const_cast(value.impl()); + AK::retain_if_not_null(m_value.as_string); + } +} + +JsonValue::JsonValue(const JsonObject& value) + : m_type(Type::Object) +{ + m_value.as_object = new JsonObject(value); +} + +JsonValue::JsonValue(const JsonArray& value) + : m_type(Type::Array) +{ + m_value.as_array = new JsonArray(value); +} + +void JsonValue::clear() +{ + switch (m_type) { + case Type::String: + AK::release_if_not_null(m_value.as_string); + break; + case Type::Object: + delete m_value.as_object; + break; + case Type::Array: + delete m_value.as_array; + break; + default: + break; + } + m_type = Type::Undefined; + m_value.as_string = nullptr; +} + +String JsonValue::to_string() const +{ + switch (m_type) { + case Type::String: + return String::format("\"%s\"", m_value.as_string->characters()); + case Type::Array: + return m_value.as_array->to_string(); + case Type::Object: + return m_value.as_object->to_string(); + case Type::Bool: + return m_value.as_bool ? "true" : "false"; + case Type::Double: + return String::format("%g", m_value.as_double); + case Type::Int: + return String::format("%d", m_value.as_int); + case Type::Undefined: + return "undefined"; + case Type::Null: + return "null"; + } + ASSERT_NOT_REACHED(); +} diff --git a/AK/JsonValue.h b/AK/JsonValue.h new file mode 100644 index 00000000000..5bdfb9a5d79 --- /dev/null +++ b/AK/JsonValue.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +class JsonArray; +class JsonObject; + +class JsonValue { +public: + enum class Type { + Undefined, + Null, + Int, + Double, + Bool, + String, + Array, + Object, + }; + + explicit JsonValue(Type = Type::Null); + ~JsonValue() { clear(); } + + JsonValue(const JsonValue&); + JsonValue(JsonValue&&); + + JsonValue& operator=(const JsonValue&); + JsonValue& operator=(JsonValue&&); + + JsonValue(int); + JsonValue(double); + JsonValue(bool); + JsonValue(const String&); + JsonValue(const JsonArray&); + JsonValue(const JsonObject&); + + String to_string() const; + +private: + void clear(); + void copy_from(const JsonValue&); + + Type m_type { Type::Undefined }; + + union { + StringImpl* as_string { nullptr }; + JsonArray* as_array; + JsonObject* as_object; + double as_double; + int as_int; + bool as_bool; + } m_value; +}; diff --git a/LibC/Makefile b/LibC/Makefile index 97048382d5a..c2822e2df84 100644 --- a/LibC/Makefile +++ b/LibC/Makefile @@ -7,6 +7,9 @@ AK_OBJS = \ ../AK/StringBuilder.o \ ../AK/FileSystemPath.o \ ../AK/StdLibExtras.o \ + ../AK/JsonValue.o \ + ../AK/JsonArray.o \ + ../AK/JsonObject.o \ ../AK/MappedFile.o LIBC_OBJS = \