62 appendU16(kFormatVersion);
63 appendU32(processorId);
64 appendU16(processorVersion);
65 countPos_ = blob_.size();
70 void write(
const char* key,
float value)
73 std::memcpy(&bits, &value, 4);
74 writeEntry(key, 0, bits);
78 void write(
const char* key, int32_t value)
80 writeEntry(key, 1,
static_cast<uint32_t
>(value));
84 void write(
const char* key,
bool value)
86 writeEntry(key, 2, value ? 1u : 0u);
90 void write(
const char* key,
const std::vector<uint8_t>& nested)
92 const size_t len = std::min<size_t>(std::strlen(key), 255);
93 assert(len == std::strlen(key) &&
"state key longer than 255 chars is truncated");
94 assert(count_ < 0xFFFF &&
"state entry count overflows u16");
95 blob_.push_back(
static_cast<uint8_t
>(len));
98 appendU32(
static_cast<uint32_t
>(nested.size()));
99 blob_.insert(blob_.end(), nested.begin(), nested.end());
104 [[nodiscard]] std::vector<uint8_t>
blob()
const
106 std::vector<uint8_t> out = blob_;
107 out[countPos_] =
static_cast<uint8_t
>(count_ & 0xFF);
108 out[countPos_ + 1] =
static_cast<uint8_t
>((count_ >> 8) & 0xFF);
113 static constexpr uint16_t kFormatVersion = 1;
115 void writeEntry(
const char* key, uint8_t type, uint32_t payload)
117 const size_t len = std::min<size_t>(std::strlen(key), 255);
118 assert(len == std::strlen(key) &&
"state key longer than 255 chars is truncated");
119 assert(count_ < 0xFFFF &&
"state entry count overflows u16");
120 blob_.push_back(
static_cast<uint8_t
>(len));
122 blob_.push_back(type);
127 void append(
const char* data,
size_t n)
129 blob_.insert(blob_.end(), data, data + n);
131 void appendU16(uint16_t v)
133 blob_.push_back(
static_cast<uint8_t
>(v & 0xFF));
134 blob_.push_back(
static_cast<uint8_t
>(v >> 8));
136 void appendU32(uint32_t v)
138 for (
int i = 0; i < 4; ++i)
139 blob_.push_back(
static_cast<uint8_t
>((v >> (8 * i)) & 0xFF));
142 std::vector<uint8_t> blob_;
143 size_t countPos_ = 0;
164 if (data ==
nullptr || size < 14 || std::memcmp(data,
"DSPK", 4) != 0)
166 const uint16_t fmt = readU16(data + 4);
167 if (fmt != 1)
return;
168 processorId_ = readU32(data + 6);
169 processorVersion_ = readU16(data + 10);
170 const uint16_t count = readU16(data + 12);
173 for (uint16_t i = 0; i < count; ++i)
175 if (pos + 1 > size)
return;
176 const size_t keyLen = data[pos++];
177 if (pos + keyLen + 5 > size)
return;
179 e.
key.assign(
reinterpret_cast<const char*
>(data + pos), keyLen);
181 e.
type = data[pos++];
182 e.
payload = readU32(data + pos);
189 if (e.
payload > size - pos)
return;
193 entries_.push_back(std::move(e));
198 [[nodiscard]]
bool isValid() const noexcept {
return valid_; }
199 [[nodiscard]] uint32_t
processorId() const noexcept {
return processorId_; }
203 [[nodiscard]]
float read(
const char* key,
float defaultValue)
const
205 if (
const Entry* e = find(key, 0))
208 std::memcpy(&v, &e->payload, 4);
215 [[nodiscard]] int32_t
read(
const char* key, int32_t defaultValue)
const
217 if (
const Entry* e = find(key, 1))
218 return static_cast<int32_t
>(e->payload);
223 [[nodiscard]]
bool read(
const char* key,
bool defaultValue)
const
225 if (
const Entry* e = find(key, 2))
226 return e->payload != 0;
231 [[nodiscard]] std::vector<uint8_t>
readBlob(
const char* key)
const
233 if (
const Entry* e = find(key, 3))
246 [[nodiscard]]
const std::vector<Entry>&
entries() const noexcept {
return entries_; }
249 [[nodiscard]]
const Entry* find(
const char* key, uint8_t type)
const
251 for (
const auto& e : entries_)
252 if (e.type == type && e.key == key)
257 static uint16_t readU16(
const uint8_t* p)
259 return static_cast<uint16_t
>(p[0] | (p[1] << 8));
261 static uint32_t readU32(
const uint8_t* p)
263 return static_cast<uint32_t
>(p[0]) | (
static_cast<uint32_t
>(p[1]) << 8)
264 | (
static_cast<uint32_t
>(p[2]) << 16) | (
static_cast<uint32_t
>(p[3]) << 24);
268 uint32_t processorId_ = 0;
269 uint16_t processorVersion_ = 0;
270 std::vector<Entry> entries_;
291 for (
const unsigned char c : s)
295 case '"': out +=
"\\\"";
break;
296 case '\\': out +=
"\\\\";
break;
297 case '\b': out +=
"\\b";
break;
298 case '\f': out +=
"\\f";
break;
299 case '\n': out +=
"\\n";
break;
300 case '\r': out +=
"\\r";
break;
301 case '\t': out +=
"\\t";
break;
306 std::snprintf(esc,
sizeof(esc),
"\\u%04x",
static_cast<unsigned>(c));
311 out +=
static_cast<char>(c);
324 if (!std::isfinite(v)) { out +=
'0';
return; }
326 std::snprintf(num,
sizeof(num),
"%.9g",
static_cast<double>(v));
327 for (
char* c = num; *c !=
'\0'; ++c)
328 if (*c ==
',') *c =
'.';
335 bool negative =
false;
336 if (s < end && (*s ==
'-' || *s ==
'+'))
338 negative = (*s ==
'-');
343 while (s < end && *s >=
'0' && *s <=
'9')
345 v = v * 10.0 + (*s -
'0');
349 if (s < end && *s ==
'.')
353 while (s < end && *s >=
'0' && *s <=
'9')
361 if (!any)
return nullptr;
362 if (s < end && (*s ==
'e' || *s ==
'E'))
365 bool expNegative =
false;
366 if (s < end && (*s ==
'-' || *s ==
'+'))
368 expNegative = (*s ==
'-');
373 while (s < end && *s >=
'0' && *s <=
'9')
375 e = e * 10 + (*s -
'0');
378 if (e > 308) { e = 309; }
380 if (!eAny)
return nullptr;
382 for (
int k = 0; k < (e > 309 ? 309 : e); ++k)
384 v = expNegative ? v / scale : v * scale;
386 out = negative ? -v : v;
397 std::string out =
"{\"id\":" + std::to_string(r.
processorId())
401 for (
const auto& e : r.
entries())
403 if (!first) out +=
',';
410 std::memcpy(&v, &e.payload, 4);
413 else if (e.type == 1)
415 out += std::to_string(
static_cast<int32_t
>(e.payload));
417 else if (e.type == 3)
423 out += (e.payload != 0) ?
"true" :
"false";
434 auto skipWs = [&](
size_t& p) {
while (p < json.size() && (json[p] ==
' ' || json[p] ==
'\n'
435 || json[p] ==
'\t' || json[p] ==
'\r')) ++p; };
436 auto expect = [&](
size_t& p,
char c) {
438 if (p >= json.size() || json[p] != c)
return false;
442 auto parseString = [&](
size_t& p, std::string& out) {
444 if (p >= json.size() || json[p] !=
'"')
return false;
447 while (p < json.size() && json[p] !=
'"')
452 if (p >= json.size())
return false;
453 const char e = json[p++];
456 case '"': out +=
'"';
break;
457 case '\\': out +=
'\\';
break;
458 case '/': out +=
'/';
break;
459 case 'b': out +=
'\b';
break;
460 case 'f': out +=
'\f';
break;
461 case 'n': out +=
'\n';
break;
462 case 'r': out +=
'\r';
break;
463 case 't': out +=
'\t';
break;
466 if (p + 4 > json.size())
return false;
468 for (
int k = 0; k < 4; ++k)
470 const char h = json[p++];
472 if (h >=
'0' && h <=
'9') code |= unsigned(h -
'0');
473 else if (h >=
'a' && h <=
'f') code |= unsigned(h -
'a' + 10);
474 else if (h >=
'A' && h <=
'F') code |= unsigned(h -
'A' + 10);
479 out +=
static_cast<char>(code & 0xFF);
482 default:
return false;
490 if (p >= json.size())
return false;
494 auto parseNumber = [&](
size_t& p,
double& out) {
496 const char* start = json.c_str() + p;
497 const char* endOfJson = json.c_str() + json.size();
499 if (stop ==
nullptr)
return false;
500 p =
static_cast<size_t>(stop - json.c_str());
508 uint16_t version = 0;
510 if (!expect(p,
'{'))
return {};
511 struct Param { std::string key; uint8_t type; uint32_t payload;
512 std::vector<uint8_t> bytes; };
513 std::vector<Param> params;
517 if (!parseString(p, key) || !expect(p,
':'))
return {};
518 if (key ==
"id" && parseNumber(p, num))
520 id =
static_cast<uint32_t
>(num);
522 else if (key ==
"version" && parseNumber(p, num))
524 version =
static_cast<uint16_t
>(num);
526 else if (key ==
"params")
528 if (!expect(p,
'{'))
return {};
530 if (p < json.size() && json[p] ==
'}') { ++p; }
536 if (!parseString(p, pk) || !expect(p,
':'))
return {};
538 Param prm { pk, 0, 0, {} };
539 if (p < json.size() && json[p] ==
'{')
542 size_t braceDepth = 0, end = p;
543 for (; end < json.size(); ++end)
545 if (json[end] ==
'{') ++braceDepth;
546 else if (json[end] ==
'}' && --braceDepth == 0) { ++end;
break; }
548 if (braceDepth != 0)
return {};
551 if (prm.bytes.empty())
return {};
554 else if (json.compare(p, 4,
"true") == 0)
556 prm.type = 2; prm.payload = 1; p += 4;
558 else if (json.compare(p, 5,
"false") == 0)
560 prm.type = 2; prm.payload = 0; p += 5;
562 else if (parseNumber(p, num))
566 const auto f =
static_cast<float>(num);
567 std::memcpy(&prm.payload, &f, 4);
569 if (num ==
static_cast<double>(
static_cast<int32_t
>(num)))
571 params.push_back({ pk, 1,
572 static_cast<uint32_t
>(
static_cast<int32_t
>(num)), {} });
576 params.push_back(std::move(prm));
578 if (p < json.size() && json[p] ==
',') { ++p;
continue; }
581 if (!expect(p,
'}'))
return {};
586 if (p < json.size() && json[p] ==
',') { ++p;
continue; }
589 if (!expect(p,
'}'))
return {};
592 for (
const auto& prm : params)
597 std::memcpy(&v, &prm.payload, 4);
598 w.
write(prm.key.c_str(), v);
600 else if (prm.type == 1)
602 w.
write(prm.key.c_str(),
static_cast<int32_t
>(prm.payload));
604 else if (prm.type == 3)
606 w.
write(prm.key.c_str(), prm.bytes);
610 w.
write(prm.key.c_str(), prm.payload != 0);
619[[nodiscard]]
inline std::string
stateToJson(
const std::vector<uint8_t>& blob)
633[[nodiscard]]
inline std::vector<uint8_t>
stateFromJson(
const std::string& json)
639[[nodiscard]]
constexpr uint32_t
stateId(
const char (&tag)[5])
noexcept
643 return static_cast<uint32_t
>(
static_cast<uint8_t
>(tag[0]))
644 | (
static_cast<uint32_t
>(
static_cast<uint8_t
>(tag[1])) << 8)
645 | (
static_cast<uint32_t
>(
static_cast<uint8_t
>(tag[2])) << 16)
646 | (
static_cast<uint32_t
>(
static_cast<uint8_t
>(tag[3])) << 24);
Tolerant reader: missing keys yield defaults, unknown keys are skipped.
std::vector< uint8_t > readBlob(const char *key) const
Reads a nested blob; empty when the key is absent.
StateReader(const uint8_t *data, size_t size)
int32_t read(const char *key, int32_t defaultValue) const
Reads an int32, or defaultValue when the key is absent.
uint16_t processorVersion() const noexcept
float read(const char *key, float defaultValue) const
Reads a float, or defaultValue when the key is absent.
bool isValid() const noexcept
uint32_t processorId() const noexcept
const std::vector< Entry > & entries() const noexcept
bool read(const char *key, bool defaultValue) const
Reads a bool, or defaultValue when the key is absent.
Serializes key/value parameters into a versioned blob.
void write(const char *key, const std::vector< uint8_t > &nested)
Writes a nested blob (composite processors: bands, slots...).
StateWriter(uint32_t processorId, uint16_t processorVersion)
std::vector< uint8_t > blob() const
Finalizes and returns the blob.
void write(const char *key, float value)
Writes a float parameter.
void write(const char *key, bool value)
Writes a boolean parameter.
void write(const char *key, int32_t value)
Writes an integer parameter (enums, counts, modes).
const char * parseStateJsonNumber(const char *s, const char *end, double &out) noexcept
Locale-independent number parse over [s, end); nullptr on failure.
std::string stateToJsonImpl(const std::vector< uint8_t > &blob, int depth)
std::vector< uint8_t > stateFromJsonImpl(const std::string &json, int depth)
void appendStateJsonNumber(std::string &out, float v)
Locale-independent float rendering ("%.9g" with a forced dot). Non-finite payloads (NaN/Inf – e....
constexpr int kMaxStateJsonDepth
void appendStateJsonString(std::string &out, const std::string &s)
Appends a JSON string literal, escaping the characters JSON forbids. Keys follow the [A-Za-z0-9....
Main namespace for the DSPark framework.
std::string stateToJson(const std::vector< uint8_t > &blob)
Renders a state blob as a flat JSON object string.
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
std::vector< uint8_t > stateFromJson(const std::string &json)
Parses the JSON produced by stateToJson() back into a blob.
Access for generic tooling (JSON rendering, preset browsers).
std::vector< uint8_t > bytes
Nested blob content (type 3).