Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
script_value.cpp
Go to the documentation of this file.
1// script_value.cpp — ScriptValue(Luau の値の C++ の木)の並べ方・バイト列・ハッシュ(T-0138、ADR-0031)。
3
4#include <algorithm>
5#include <bit>
6#include <format>
7#include <utility>
8
9namespace bicameral::script {
10
11 namespace {
12
13 void AppendUint32(uint32_t value, std::string& out) {
14 for (int shift = 0; shift < 32; shift += 8)
15 out.push_back(static_cast<char>((value >> shift) & 0xFFu));
16 }
17
18 void AppendUint64(uint64_t value, std::string& out) {
19 for (int shift = 0; shift < 64; shift += 8)
20 out.push_back(static_cast<char>((value >> shift) & 0xFFu));
21 }
22
23 } // namespace
24
25 // --- 作る ---
26
28 ScriptValue result;
29 result.kind = Kind::Boolean;
30 result.boolean = value;
31
32 return result;
33 }
34
36 ScriptValue result;
37 result.kind = Kind::Number;
38 result.number = value;
39
40 return result;
41 }
42
44 ScriptValue result;
45 result.kind = Kind::String;
46 result.text = std::move(value);
47
48 return result;
49 }
50
51 ScriptValue ScriptValue::MakeTable(std::vector<ScriptField> fields) {
52 std::ranges::sort(
53 fields, [](const ScriptField& left, const ScriptField& right) { return KeyLess(left.key, right.key); });
54
55 ScriptValue result;
56 result.kind = Kind::Table;
57 result.fields = std::move(fields);
58
59 return result;
60 }
61
62 // --- 読む ---
63
64 const ScriptValue* ScriptValue::Find(std::string_view key) const {
65 for (const ScriptField& field : fields) {
66 if (field.key.kind == Kind::String && field.key.text == key)
67 return &field.value;
68 }
69
70 return nullptr;
71 }
72
73 bool KeyLess(const ScriptValue& left, const ScriptValue& right) {
74 if (left.kind != right.kind)
75 return left.kind < right.kind; // Number < String(Kind の並び)
77 return left.number < right.number;
78
79 return left.text < right.text; // char_traits<char> の比較はバイト(unsigned char)順
80 }
81
82 // --- バイト列 ---
83
84 void AppendCanonicalString(std::string_view text, std::string& out) {
85 AppendUint32(static_cast<uint32_t>(text.size()), out);
86 out.append(text);
87 }
88
89 void AppendCanonicalBytes(const ScriptValue& value, std::string& out) {
90 out.push_back(static_cast<char>(value.kind));
91
92 switch (value.kind) {
93 case ScriptValue::Kind::Nil: break;
94 case ScriptValue::Kind::Boolean: out.push_back(value.boolean ? 1 : 0); break;
95 case ScriptValue::Kind::Number: AppendUint64(std::bit_cast<uint64_t>(value.number), out); break;
98 AppendUint32(static_cast<uint32_t>(value.fields.size()), out);
99 for (const ScriptField& field : value.fields) {
100 AppendCanonicalBytes(field.key, out);
101 AppendCanonicalBytes(field.value, out);
102 }
103 break;
104 }
105 }
106
107 uint64_t HashBytes(std::string_view bytes) {
108 constexpr uint64_t FNV_OFFSET = 14695981039346656037ull;
109 constexpr uint64_t FNV_PRIME = 1099511628211ull;
110
111 uint64_t hash = FNV_OFFSET;
112 for (const char byte : bytes) {
113 hash ^= static_cast<uint8_t>(byte);
114 hash *= FNV_PRIME;
115 }
116
117 return hash;
118 }
119
120 // --- 人が読む形 ---
121
122 std::string ToDebugText(const ScriptValue& value) {
123 switch (value.kind) {
124 case ScriptValue::Kind::Nil: return "nil";
125 case ScriptValue::Kind::Boolean: return value.boolean ? "true" : "false";
126 case ScriptValue::Kind::Number: return std::format("{}", value.number);
127 case ScriptValue::Kind::String: return std::format("\"{}\"", value.text);
128 case ScriptValue::Kind::Table: break;
129 }
130
131 std::string text = "{";
132 for (size_t index = 0; index < value.fields.size(); ++index) {
133 if (index > 0)
134 text += ", ";
135 text += std::format("[{}]={}", ToDebugText(value.fields[index].key),
136 ToDebugText(value.fields[index].value));
137 }
138
139 return text + "}";
140 }
141
142} // namespace bicameral::script
void AppendUint32(uint32_t value, std::string &out)
void AppendUint64(uint64_t value, std::string &out)
std::string ToDebugText(const ScriptValue &value)
void AppendCanonicalBytes(const ScriptValue &value, std::string &out)
uint64_t HashBytes(std::string_view bytes)
bool KeyLess(const ScriptValue &left, const ScriptValue &right)
void AppendCanonicalString(std::string_view text, std::string &out)
std::vector< ScriptField > fields
static ScriptValue MakeString(std::string value)
static ScriptValue MakeTable(std::vector< ScriptField > fields)
static ScriptValue MakeNumber(double value)
const ScriptValue * Find(std::string_view key) const
static ScriptValue MakeBoolean(bool value)