Skip to content

Commit

Permalink
add Blob serialization and test
Browse files Browse the repository at this point in the history
  • Loading branch information
madronalabs committed Jul 10, 2024
1 parent 2683e6d commit b70ea9a
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 31 deletions.
7 changes: 5 additions & 2 deletions Tests/treeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,17 +450,20 @@ TEST_CASE("madronalib/core/serialization", "[serialization]")
v["b"] = "hello";
v["a/b/c"] = "hello again";

std::array<uint8_t, 5> testArray{1, 3, 5, 7, 9};
auto testBytes = testArray.size() * sizeof(uint8_t);
v["blobtest"] = Value(testArray.data(), testBytes);

Tree< Value > v2 = JSONToValueTree(valueTreeToJSON(v));

REQUIRE(v == v2);

// Value tree to JSON to text to JSON to value tree.
auto t1 = JSONToText(valueTreeToJSON(v));
auto v3 = JSONToValueTree(textToJSON(t1));
REQUIRE(v == v3);

}


TEST_CASE("madronalib/core/floatvectors", "[floatvectors]")
{
const int kTestSize{100};
Expand Down
52 changes: 29 additions & 23 deletions source/app/MLSerialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,13 @@ inline std::unique_ptr<std::vector<uint8_t> > valueToBinary(Value v)
}
case Value::kBlobValue:
{
char* blobData = static_cast<char*>(v.getBlobValue());
uint8_t* blobData = v.getBlobData();
unsigned int blobSize = (unsigned int)v.getBlobSize();
outputVector.resize(headerSize + blobSize);
BinaryChunkHeader* header{reinterpret_cast<BinaryChunkHeader*>(outputVector.data())};
*header = BinaryChunkHeader{'B', blobSize};
auto pDest{outputVector.data() + headerSize};
auto pSrc{blobData};
std::copy(pSrc, pSrc + blobSize, pDest);
std::copy(blobData, blobData + blobSize, pDest);
break;
}
}
Expand Down Expand Up @@ -444,6 +443,8 @@ class JSONHolder
cJSON* data() { return &_data; }
};

static TextFragment kBlobHeader("!BLOB!");

// return a JSON object representing the value tree. The caller is responsible
// for freeing the object.
//
Expand Down Expand Up @@ -471,28 +472,18 @@ inline JSONHolder valueTreeToJSON(const Tree<Value>& t)
case Value::kTextValue:
cJSON_AddStringToObject(root.data(), keyStr, v.getTextValue().getText());
break;
case Value::kMatrixValue:
{
/* TODO
cJSON* signalObj = cJSON_CreateObject();
const MLSignal& sig = state.mValue.getSignalValue();
cJSON_AddStringToObject(signalObj, "type", "signal");
cJSON_AddNumberToObject(signalObj, "width", sig.getWidth());
cJSON_AddNumberToObject(signalObj, "height", sig.getHeight());
cJSON_AddNumberToObject(signalObj, "depth", sig.getDepth());
int size = sig.getSize();
float* pSignalData = sig.getBuffer();
cJSON* data = cJSON_CreateFloatArray(pSignalData, size);
cJSON_AddItemToObject(signalObj, "data", data);
// add signal object to state JSON
cJSON_AddItemToObject(root, keyStr, signalObj);
*/
}
break;
case Value::kUnsignedLongValue:
cJSON_AddNumberToObject(root.data(), keyStr, v.getUnsignedLongValue());
break;
case Value::kBlobValue:
{
uint8_t* blobData = v.getBlobData();
size_t blobSize = v.getBlobSize();
std::vector<uint8_t> blobVec(blobData, blobData + blobSize);
TextFragment blobText(kBlobHeader, textUtils::base64Encode(blobVec));
cJSON_AddStringToObject(root.data(), keyStr, blobText.getText());
break;
}
default:
// debug() << "MLAppState::saveStateToStateFile(): undefined param type! \n";
break;
Expand Down Expand Up @@ -527,7 +518,22 @@ inline Tree<Value> readJSONToValueTree(cJSON* obj, Tree<Value>& r, Path currentP
}
case cJSON_String:
{
r.add(newObjectPath, TextFragment(obj->valuestring));
TextFragment valueText(obj->valuestring);
if(valueText.beginsWith(kBlobHeader)) // not wonderful
{
auto headerLen = kBlobHeader.lengthInCodePoints();
auto textLen = valueText.lengthInCodePoints();
auto body = textUtils::subText(valueText, headerLen, textLen);

auto blobDataVec = textUtils::base64Decode(body.getText());
auto* pBlobData{reinterpret_cast<const void*>(blobDataVec.data())};
auto blobValue = Value{pBlobData, blobDataVec.size()};
r.add(newObjectPath, blobValue);
}
else
{
r.add(newObjectPath, valueText);
}
break;
}
case cJSON_Object:
Expand Down
5 changes: 3 additions & 2 deletions source/app/MLValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ bool Value::operator==(const Value& b) const
r = (getUnsignedLongValue() == b.getUnsignedLongValue());
break;
case kBlobValue:
r = false;
// compare blobs by value
r = !std::memcmp(getBlobData(), b.getBlobData(), getBlobSize());
break;
case kIntervalValue:
r = (getIntervalValue() == b.getIntervalValue());
Expand Down Expand Up @@ -291,7 +292,7 @@ std::ostream& operator<<(std::ostream& out, const Value& r)
out << r.getUnsignedLongValue();
break;
case Value::kBlobValue:
out << "[blob]";
out << "[blob, " << r.getBlobSize() << " bytes]";
break;
case Value::kIntervalValue:
out << r.getIntervalValue();
Expand Down
9 changes: 5 additions & 4 deletions source/app/MLValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ class Value
Value(const ml::Matrix& s);
Value(Interval i);

// binary blob constructor.
// if data size > kBlobSizeBytes, this will allocate heap.
// Blob constructors.
// if data size > kBlobSizeBytes, blob values will allocate heap.
explicit Value(const void* pData, size_t n);
// TODO make array-like ctor using gsl::span

// matrix type constructor via initializer_list
Value(std::initializer_list<float> values)
Expand Down Expand Up @@ -141,11 +142,11 @@ class Value
return (mType == kIntervalValue) ? (mIntervalVal) : d;
}

inline void* getBlobValue() const
inline uint8_t* getBlobData() const
{
if (mType == kBlobValue)
{
return (void*)(pBlobData);
return pBlobData;
}
else
{
Expand Down

0 comments on commit b70ea9a

Please sign in to comment.