63 appendU16(kFormatVersion);
64 appendU32(processorId);
65 appendU16(processorVersion);
66 countPos_ = blob_.size();
71 void write(
const char* key,
float value)
74 std::memcpy(&bits, &value, 4);
75 writeEntry(key, 0, bits);
79 void write(
const char* key, int32_t value)
81 writeEntry(key, 1,
static_cast<uint32_t
>(value));
85 void write(
const char* key,
bool value)
87 writeEntry(key, 2, value ? 1u : 0u);
91 void write(
const char* key,
const std::vector<uint8_t>& nested)
93 const size_t len = std::min<size_t>(std::strlen(key), 255);
94 assert(len == std::strlen(key) &&
"state key longer than 255 chars is truncated");
95 assert(count_ < 0xFFFF &&
"state entry count overflows u16");
96 blob_.push_back(
static_cast<uint8_t
>(len));
99 appendU32(
static_cast<uint32_t
>(nested.size()));
100 blob_.insert(blob_.end(), nested.begin(), nested.end());
105 [[nodiscard]] std::vector<uint8_t>
blob()
const
107 std::vector<uint8_t> out = blob_;
108 out[countPos_] =
static_cast<uint8_t
>(count_ & 0xFF);
109 out[countPos_ + 1] =
static_cast<uint8_t
>((count_ >> 8) & 0xFF);
114 static constexpr uint16_t kFormatVersion = 1;
116 void writeEntry(
const char* key, uint8_t type, uint32_t payload)
118 const size_t len = std::min<size_t>(std::strlen(key), 255);
119 assert(len == std::strlen(key) &&
"state key longer than 255 chars is truncated");
120 assert(count_ < 0xFFFF &&
"state entry count overflows u16");
121 blob_.push_back(
static_cast<uint8_t
>(len));
123 blob_.push_back(type);
128 void append(
const char* data,
size_t n)
130 blob_.insert(blob_.end(), data, data + n);
132 void appendU16(uint16_t v)
134 blob_.push_back(
static_cast<uint8_t
>(v & 0xFF));
135 blob_.push_back(
static_cast<uint8_t
>(v >> 8));
137 void appendU32(uint32_t v)
139 for (
int i = 0; i < 4; ++i)
140 blob_.push_back(
static_cast<uint8_t
>((v >> (8 * i)) & 0xFF));
143 std::vector<uint8_t> blob_;
144 size_t countPos_ = 0;
165 if (data ==
nullptr || size < 14 || std::memcmp(data,
"DSPK", 4) != 0)
167 const uint16_t fmt = readU16(data + 4);
168 if (fmt != 1)
return;
169 processorId_ = readU32(data + 6);
170 processorVersion_ = readU16(data + 10);
171 const uint16_t count = readU16(data + 12);
174 for (uint16_t i = 0; i < count; ++i)
176 if (pos + 1 > size)
return;
177 const size_t keyLen = data[pos++];
178 if (pos + keyLen + 5 > size)
return;
180 e.
key.assign(
reinterpret_cast<const char*
>(data + pos), keyLen);
182 e.
type = data[pos++];
183 e.
payload = readU32(data + pos);
190 if (e.
payload > size - pos)
return;
194 entries_.push_back(std::move(e));
199 [[nodiscard]]
bool isValid() const noexcept {
return valid_; }
200 [[nodiscard]] uint32_t
processorId() const noexcept {
return processorId_; }
204 [[nodiscard]]
float read(
const char* key,
float defaultValue)
const
206 if (
const Entry* e = find(key, 0))
209 std::memcpy(&v, &e->payload, 4);
216 [[nodiscard]] int32_t
read(
const char* key, int32_t defaultValue)
const
218 if (
const Entry* e = find(key, 1))
219 return static_cast<int32_t
>(e->payload);
224 [[nodiscard]]
bool read(
const char* key,
bool defaultValue)
const
226 if (
const Entry* e = find(key, 2))
227 return e->payload != 0;
232 [[nodiscard]] std::vector<uint8_t>
readBlob(
const char* key)
const
234 if (
const Entry* e = find(key, 3))
247 [[nodiscard]]
const std::vector<Entry>&
entries() const noexcept {
return entries_; }
250 [[nodiscard]]
const Entry* find(
const char* key, uint8_t type)
const
252 for (
const auto& e : entries_)
253 if (e.type == type && e.key == key)
258 static uint16_t readU16(
const uint8_t* p)
260 return static_cast<uint16_t
>(p[0] | (p[1] << 8));
262 static uint32_t readU32(
const uint8_t* p)
264 return static_cast<uint32_t
>(p[0]) | (
static_cast<uint32_t
>(p[1]) << 8)
265 | (
static_cast<uint32_t
>(p[2]) << 16) | (
static_cast<uint32_t
>(p[3]) << 24);
269 uint32_t processorId_ = 0;
270 uint16_t processorVersion_ = 0;
271 std::vector<Entry> entries_;
292 for (
const unsigned char c : s)
296 case '"': out +=
"\\\"";
break;
297 case '\\': out +=
"\\\\";
break;
298 case '\b': out +=
"\\b";
break;
299 case '\f': out +=
"\\f";
break;
300 case '\n': out +=
"\\n";
break;
301 case '\r': out +=
"\\r";
break;
302 case '\t': out +=
"\\t";
break;
307 std::snprintf(esc,
sizeof(esc),
"\\u%04x",
static_cast<unsigned>(c));
312 out +=
static_cast<char>(c);
325 if (!std::isfinite(v)) { out +=
'0';
return; }
327 std::snprintf(num,
sizeof(num),
"%.9g",
static_cast<double>(v));
328 for (
char* c = num; *c !=
'\0'; ++c)
329 if (*c ==
',') *c =
'.';
336 bool negative =
false;
337 if (s < end && (*s ==
'-' || *s ==
'+'))
339 negative = (*s ==
'-');
344 while (s < end && *s >=
'0' && *s <=
'9')
346 v = v * 10.0 + (*s -
'0');
350 if (s < end && *s ==
'.')
354 while (s < end && *s >=
'0' && *s <=
'9')
362 if (!any)
return nullptr;
363 if (s < end && (*s ==
'e' || *s ==
'E'))
366 bool expNegative =
false;
367 if (s < end && (*s ==
'-' || *s ==
'+'))
369 expNegative = (*s ==
'-');
374 while (s < end && *s >=
'0' && *s <=
'9')
376 e = e * 10 + (*s -
'0');
379 if (e > 308) { e = 309; }
381 if (!eAny)
return nullptr;
383 for (
int k = 0; k < (e > 309 ? 309 : e); ++k)
385 v = expNegative ? v / scale : v * scale;
387 out = negative ? -v : v;
398 std::string out =
"{\"id\":" + std::to_string(r.
processorId())
402 for (
const auto& e : r.
entries())
404 if (!first) out +=
',';
411 std::memcpy(&v, &e.payload, 4);
414 else if (e.type == 1)
416 out += std::to_string(
static_cast<int32_t
>(e.payload));
418 else if (e.type == 3)
424 out += (e.payload != 0) ?
"true" :
"false";
435 auto skipWs = [&](
size_t& p) {
while (p < json.size() && (json[p] ==
' ' || json[p] ==
'\n'
436 || json[p] ==
'\t' || json[p] ==
'\r')) ++p; };
437 auto expect = [&](
size_t& p,
char c) {
439 if (p >= json.size() || json[p] != c)
return false;
443 auto parseString = [&](
size_t& p, std::string& out) {
445 if (p >= json.size() || json[p] !=
'"')
return false;
448 while (p < json.size() && json[p] !=
'"')
453 if (p >= json.size())
return false;
454 const char e = json[p++];
457 case '"': out +=
'"';
break;
458 case '\\': out +=
'\\';
break;
459 case '/': out +=
'/';
break;
460 case 'b': out +=
'\b';
break;
461 case 'f': out +=
'\f';
break;
462 case 'n': out +=
'\n';
break;
463 case 'r': out +=
'\r';
break;
464 case 't': out +=
'\t';
break;
467 if (p + 4 > json.size())
return false;
469 for (
int k = 0; k < 4; ++k)
471 const char h = json[p++];
473 if (h >=
'0' && h <=
'9') code |= unsigned(h -
'0');
474 else if (h >=
'a' && h <=
'f') code |= unsigned(h -
'a' + 10);
475 else if (h >=
'A' && h <=
'F') code |= unsigned(h -
'A' + 10);
480 out +=
static_cast<char>(code & 0xFF);
483 default:
return false;
491 if (p >= json.size())
return false;
495 auto parseNumber = [&](
size_t& p,
double& out) {
497 const char* start = json.c_str() + p;
498 const char* endOfJson = json.c_str() + json.size();
500 if (stop ==
nullptr)
return false;
501 p =
static_cast<size_t>(stop - json.c_str());
509 uint16_t version = 0;
511 if (!expect(p,
'{'))
return {};
512 struct Param { std::string key; uint8_t type; uint32_t payload;
513 std::vector<uint8_t> bytes; };
514 std::vector<Param> params;
518 if (!parseString(p, key) || !expect(p,
':'))
return {};
519 if (key ==
"id" && parseNumber(p, num))
524 if (!(num >= 0.0 && num <= 4294967295.0))
return {};
525 id =
static_cast<uint32_t
>(num);
527 else if (key ==
"version" && parseNumber(p, num))
529 if (!(num >= 0.0 && num <= 65535.0))
return {};
530 version =
static_cast<uint16_t
>(num);
532 else if (key ==
"params")
534 if (!expect(p,
'{'))
return {};
536 if (p < json.size() && json[p] ==
'}') { ++p; }
542 if (!parseString(p, pk) || !expect(p,
':'))
return {};
544 Param prm { pk, 0, 0, {} };
545 if (p < json.size() && json[p] ==
'{')
548 size_t braceDepth = 0, end = p;
549 for (; end < json.size(); ++end)
551 if (json[end] ==
'{') ++braceDepth;
552 else if (json[end] ==
'}' && --braceDepth == 0) { ++end;
break; }
554 if (braceDepth != 0)
return {};
557 if (prm.bytes.empty())
return {};
560 else if (json.compare(p, 4,
"true") == 0)
562 prm.type = 2; prm.payload = 1; p += 4;
564 else if (json.compare(p, 5,
"false") == 0)
566 prm.type = 2; prm.payload = 0; p += 5;
568 else if (parseNumber(p, num))
574 if (!(std::abs(num) <=
static_cast<double>(
575 std::numeric_limits<float>::max())))
577 const auto f =
static_cast<float>(num);
578 std::memcpy(&prm.payload, &f, 4);
580 if (num >= -2147483648.0 && num <= 2147483647.0
581 && num ==
static_cast<double>(
static_cast<int32_t
>(num)))
583 params.push_back({ pk, 1,
584 static_cast<uint32_t
>(
static_cast<int32_t
>(num)), {} });
588 params.push_back(std::move(prm));
590 if (p < json.size() && json[p] ==
',') { ++p;
continue; }
593 if (!expect(p,
'}'))
return {};
598 if (p < json.size() && json[p] ==
',') { ++p;
continue; }
601 if (!expect(p,
'}'))
return {};
604 for (
const auto& prm : params)
609 std::memcpy(&v, &prm.payload, 4);
610 w.
write(prm.key.c_str(), v);
612 else if (prm.type == 1)
614 w.
write(prm.key.c_str(),
static_cast<int32_t
>(prm.payload));
616 else if (prm.type == 3)
618 w.
write(prm.key.c_str(), prm.bytes);
622 w.
write(prm.key.c_str(), prm.payload != 0);
631[[nodiscard]]
inline std::string
stateToJson(
const std::vector<uint8_t>& blob)
645[[nodiscard]]
inline std::vector<uint8_t>
stateFromJson(
const std::string& json)
651[[nodiscard]]
constexpr uint32_t
stateId(
const char (&tag)[5])
noexcept
655 return static_cast<uint32_t
>(
static_cast<uint8_t
>(tag[0]))
656 | (
static_cast<uint32_t
>(
static_cast<uint8_t
>(tag[1])) << 8)
657 | (
static_cast<uint32_t
>(
static_cast<uint8_t
>(tag[2])) << 16)
658 | (
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).