mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
Remove PODTimeSamples and simplify TypedArray/TimeSamples architecture
This refactoring removes the deprecated PODTimeSamples class and simplifies the TypedArray hierarchy: TypedArray changes: - Rename TypedArrayImpl to TypedArray (main storage class) - Rename TypedArray to TypedArrayPtr (packed pointer wrapper for dedup/mmap) - Remove is_view functionality from TypedArray (TypedArrayView handles views) - Remove MakeTypedArrayView/MakeTypedArrayMmap factory functions TimeSamples changes: - Move offset encoding constants (OFFSET_DEDUP_FLAG, etc.) to TimeSamples - Remove _use_pod and _pod_samples members from TimeSamples - Delete entire PODTimeSamples struct (~1000 lines) - Update TimeSamples::update() to use unified storage directly - Remove unused sorting helper functions File updates: - timesamples.hh: Delete PODTimeSamples, update TimeSamples methods - timesamples.cc: Remove PODTimeSamples implementations - timesamples-pprint.cc/hh: Remove PODTimeSamples printing functions - typed-array.hh/cc: Rename classes, remove is_view - crate-reader.cc: Update TypedArray references - unit-timesamples.cc: Remove PODTimeSamples-specific tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2038,7 +2038,7 @@ bool AsciiParser::ParseBasicTypeArray(TypedArray<T> *result) {
|
||||
}
|
||||
|
||||
if (c == ']') {
|
||||
(*result)->clear();
|
||||
result->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2060,10 +2060,10 @@ bool AsciiParser::ParseBasicTypeArray(TypedArray<T> *result) {
|
||||
}
|
||||
|
||||
// Transfer to TypedArray for memory optimization
|
||||
(*result)->clear();
|
||||
(*result)->reserve(temp_result.size());
|
||||
result->clear();
|
||||
result->reserve(temp_result.size());
|
||||
for (const auto& item : temp_result) {
|
||||
(*result)->push_back(item);
|
||||
result->push_back(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -859,91 +859,73 @@ bool CrateReader::ReadFloatArrayTyped(bool is_compressed, TypedArray<float> *d)
|
||||
|
||||
CHECK_MEMORY_USAGE(length * sizeof(float));
|
||||
|
||||
if (!is_compressed && _config.use_mmap) {
|
||||
// Use TypedArray view mode - no allocation, just point to mmap'd data
|
||||
uint64_t current_pos = _sr->tell();
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
d->resize(length);
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
*d = TypedArray<float>(new TypedArrayImpl<float>(const_cast<float*>(reinterpret_cast<const float*>(data_ptr)), length, true), true);
|
||||
if (!is_compressed) {
|
||||
if (!_sr->read(sizeof(float) * length, sizeof(float) * length,
|
||||
reinterpret_cast<uint8_t *>(d->data()))) {
|
||||
_err += "Failed to read float array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// Handle compressed data
|
||||
if (length < crate::kMinCompressedArraySize) {
|
||||
size_t sz = sizeof(float) * length;
|
||||
if (!_sr->read(sz, sz, reinterpret_cast<uint8_t *>(d->data()))) {
|
||||
_err += "Failed to read uncompressed array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Advance the stream position
|
||||
if (!_sr->seek_from_current(int64_t(sizeof(float) * length))) {
|
||||
_err += "Failed to advance stream position.\n";
|
||||
char code;
|
||||
if (!_sr->read1(&code)) {
|
||||
_err += "Failed to read the code.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Fall back to regular allocation for compressed data or when mmap is disabled
|
||||
(*d)->resize(length);
|
||||
|
||||
if (!is_compressed) {
|
||||
if (!_sr->read(sizeof(float) * length, sizeof(float) * length,
|
||||
reinterpret_cast<uint8_t *>((*d)->data()))) {
|
||||
_err += "Failed to read float array data.\n";
|
||||
if (code == 'i') {
|
||||
std::vector<int32_t> ints;
|
||||
ints.resize(length);
|
||||
if (!ReadCompressedInts(ints.data(), ints.size())) {
|
||||
_err += "Failed to read compressed ints in ReadFloatArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
d->data()[i] = float(ints[i]);
|
||||
}
|
||||
} else if (code == 't') {
|
||||
uint32_t lutSize;
|
||||
if (!_sr->read4(&lutSize)) {
|
||||
_err += "Failed to read lutSize in ReadFloatArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<float> lut;
|
||||
lut.resize(lutSize);
|
||||
if (!_sr->read(sizeof(float) * lutSize, sizeof(float) * lutSize,
|
||||
reinterpret_cast<uint8_t *>(lut.data()))) {
|
||||
_err += "Failed to read lut table in ReadFloatArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> indexes;
|
||||
indexes.resize(length);
|
||||
if (!ReadCompressedInts(indexes.data(), indexes.size())) {
|
||||
_err += "Failed to read lut indices in ReadFloatArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto o = d->data();
|
||||
for (auto index : indexes) {
|
||||
*o++ = lut[index];
|
||||
}
|
||||
} else {
|
||||
// Handle compressed data - same as original implementation
|
||||
if (length < crate::kMinCompressedArraySize) {
|
||||
size_t sz = sizeof(float) * length;
|
||||
if (!_sr->read(sz, sz, reinterpret_cast<uint8_t *>((*d)->data()))) {
|
||||
_err += "Failed to read uncompressed array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char code;
|
||||
if (!_sr->read1(&code)) {
|
||||
_err += "Failed to read the code.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code == 'i') {
|
||||
std::vector<int32_t> ints;
|
||||
ints.resize(length);
|
||||
if (!ReadCompressedInts(ints.data(), ints.size())) {
|
||||
_err += "Failed to read compressed ints in ReadFloatArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
(*d)->data()[i] = float(ints[i]);
|
||||
}
|
||||
} else if (code == 't') {
|
||||
uint32_t lutSize;
|
||||
if (!_sr->read4(&lutSize)) {
|
||||
_err += "Failed to read lutSize in ReadFloatArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<float> lut;
|
||||
lut.resize(lutSize);
|
||||
if (!_sr->read(sizeof(float) * lutSize, sizeof(float) * lutSize,
|
||||
reinterpret_cast<uint8_t *>(lut.data()))) {
|
||||
_err += "Failed to read lut table in ReadFloatArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> indexes;
|
||||
indexes.resize(length);
|
||||
if (!ReadCompressedInts(indexes.data(), indexes.size())) {
|
||||
_err += "Failed to read lut indices in ReadFloatArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto o = (*d)->data();
|
||||
for (auto index : indexes) {
|
||||
*o++ = lut[index];
|
||||
}
|
||||
} else {
|
||||
_err += "Invalid code. Data is corrupted\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
_err += "Invalid code. Data is corrupted\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -978,32 +960,14 @@ bool CrateReader::ReadFloat2ArrayTyped(TypedArray<value::float2> *d) {
|
||||
|
||||
CHECK_MEMORY_USAGE(length * sizeof(value::float2));
|
||||
|
||||
if (_config.use_mmap) {
|
||||
// Use TypedArray view mode - no allocation, just point to mmap'd data
|
||||
uint64_t current_pos = _sr->tell();
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
d->resize(length);
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
*d = TypedArray<value::float2>(new TypedArrayImpl<value::float2>(const_cast<value::float2*>(reinterpret_cast<const value::float2*>(data_ptr)), length, true), true);
|
||||
|
||||
// Advance the stream position
|
||||
if (!_sr->seek_from_current(int64_t(sizeof(value::float2) * length))) {
|
||||
_err += "Failed to advance stream position.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Fall back to regular allocation for compressed data or when mmap is disabled
|
||||
(*d)->resize(length);
|
||||
|
||||
if (!_sr->read(sizeof(value::float2) * length, sizeof(value::float2) * length,
|
||||
reinterpret_cast<uint8_t *>((*d)->data()))) {
|
||||
_err += "Failed to read float2 array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if (!_sr->read(sizeof(value::float2) * length, sizeof(value::float2) * length,
|
||||
reinterpret_cast<uint8_t *>(d->data()))) {
|
||||
_err += "Failed to read float2 array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TypedArray version with mmap support for double arrays
|
||||
@@ -1032,7 +996,7 @@ bool CrateReader::ReadDoubleArrayTyped(bool is_compressed, TypedArray<double> *d
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
(*d)->clear();
|
||||
d->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1042,90 +1006,72 @@ bool CrateReader::ReadDoubleArrayTyped(bool is_compressed, TypedArray<double> *d
|
||||
|
||||
CHECK_MEMORY_USAGE(length * sizeof(double));
|
||||
|
||||
if (!is_compressed && _config.use_mmap) {
|
||||
// Use TypedArray view mode - no allocation, just point to mmap'd data
|
||||
uint64_t current_pos = _sr->tell();
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
d->resize(length);
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
*d = TypedArray<double>(new TypedArrayImpl<double>(const_cast<double*>(reinterpret_cast<const double*>(data_ptr)), length, true), true);
|
||||
if (!is_compressed) {
|
||||
if (!_sr->read(sizeof(double) * length, sizeof(double) * length,
|
||||
reinterpret_cast<uint8_t *>(d->data()))) {
|
||||
_err += "Failed to read double array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// Handle compressed data
|
||||
if (length < crate::kMinCompressedArraySize) {
|
||||
size_t sz = sizeof(double) * length;
|
||||
if (!_sr->read(sz, sz, reinterpret_cast<uint8_t *>(d->data()))) {
|
||||
_err += "Failed to read uncompressed array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Advance the stream position
|
||||
if (!_sr->seek_from_current(int64_t(sizeof(double) * length))) {
|
||||
_err += "Failed to advance stream position.\n";
|
||||
char code;
|
||||
if (!_sr->read1(&code)) {
|
||||
_err += "Failed to read the code.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Fall back to regular allocation for compressed data or when mmap is disabled
|
||||
(*d)->resize(length);
|
||||
|
||||
if (!is_compressed) {
|
||||
if (!_sr->read(sizeof(double) * length, sizeof(double) * length,
|
||||
reinterpret_cast<uint8_t *>((*d)->data()))) {
|
||||
_err += "Failed to read double array data.\n";
|
||||
if (code == 'i') {
|
||||
std::vector<int64_t> ints;
|
||||
ints.resize(length);
|
||||
if (!ReadCompressedInts(ints.data(), ints.size())) {
|
||||
_err += "Failed to read compressed ints in ReadDoubleArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
std::transform(ints.begin(), ints.end(), d->data(),
|
||||
[](int64_t v) { return static_cast<double>(v); });
|
||||
} else if (code == 't') {
|
||||
uint32_t lutSize;
|
||||
if (!_sr->read4(&lutSize)) {
|
||||
_err += "Failed to read lutSize in ReadDoubleArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<double> lut;
|
||||
lut.resize(lutSize);
|
||||
if (!_sr->read(sizeof(double) * lutSize, sizeof(double) * lutSize,
|
||||
reinterpret_cast<uint8_t *>(lut.data()))) {
|
||||
_err += "Failed to read lut table in ReadDoubleArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> indexes;
|
||||
indexes.resize(length);
|
||||
if (!ReadCompressedInts(indexes.data(), indexes.size())) {
|
||||
_err += "Failed to read lut indices in ReadDoubleArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto o = d->data();
|
||||
for (auto index : indexes) {
|
||||
*o++ = lut[index];
|
||||
}
|
||||
} else {
|
||||
// Handle compressed data - same as original implementation
|
||||
if (length < crate::kMinCompressedArraySize) {
|
||||
size_t sz = sizeof(double) * length;
|
||||
if (!_sr->read(sz, sz, reinterpret_cast<uint8_t *>((*d)->data()))) {
|
||||
_err += "Failed to read uncompressed array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char code;
|
||||
if (!_sr->read1(&code)) {
|
||||
_err += "Failed to read the code.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code == 'i') {
|
||||
std::vector<int64_t> ints;
|
||||
ints.resize(length);
|
||||
if (!ReadCompressedInts(ints.data(), ints.size())) {
|
||||
_err += "Failed to read compressed ints in ReadDoubleArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
std::transform(ints.begin(), ints.end(), (*d)->data(),
|
||||
[](int64_t v) { return static_cast<double>(v); });
|
||||
} else if (code == 't') {
|
||||
uint32_t lutSize;
|
||||
if (!_sr->read4(&lutSize)) {
|
||||
_err += "Failed to read lutSize in ReadDoubleArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<double> lut;
|
||||
lut.resize(lutSize);
|
||||
if (!_sr->read(sizeof(double) * lutSize, sizeof(double) * lutSize,
|
||||
reinterpret_cast<uint8_t *>(lut.data()))) {
|
||||
_err += "Failed to read lut table in ReadDoubleArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> indexes;
|
||||
indexes.resize(length);
|
||||
if (!ReadCompressedInts(indexes.data(), indexes.size())) {
|
||||
_err += "Failed to read lut indices in ReadDoubleArrayTyped.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto o = (*d)->data();
|
||||
for (auto index : indexes) {
|
||||
*o++ = lut[index];
|
||||
}
|
||||
} else {
|
||||
_err += "Invalid code. Data is corrupted\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
_err += "Invalid code. Data is corrupted\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1156,7 +1102,7 @@ bool CrateReader::ReadIntArrayTyped(bool is_compressed, TypedArray<T> *d) {
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
(*d)->clear();
|
||||
d->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1166,40 +1112,22 @@ bool CrateReader::ReadIntArrayTyped(bool is_compressed, TypedArray<T> *d) {
|
||||
|
||||
CHECK_MEMORY_USAGE(length * sizeof(T));
|
||||
|
||||
if (!is_compressed && _config.use_mmap) {
|
||||
// Use TypedArray view mode - no allocation, just point to mmap'd data
|
||||
uint64_t current_pos = _sr->tell();
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
d->resize(length);
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
*d = TypedArray<T>(new TypedArrayImpl<T>(const_cast<T*>(reinterpret_cast<const T*>(data_ptr)), length, true), true);
|
||||
|
||||
// Advance the stream position
|
||||
if (!_sr->seek_from_current(int64_t(sizeof(T) * length))) {
|
||||
_err += "Failed to advance stream position.\n";
|
||||
if (!is_compressed) {
|
||||
if (!_sr->read(sizeof(T) * length, sizeof(T) * length,
|
||||
reinterpret_cast<uint8_t *>(d->data()))) {
|
||||
_err += "Failed to read int array data.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Fall back to regular allocation for compressed data or when mmap is disabled
|
||||
(*d)->resize(length);
|
||||
|
||||
if (!is_compressed) {
|
||||
if (!_sr->read(sizeof(T) * length, sizeof(T) * length,
|
||||
reinterpret_cast<uint8_t *>((*d)->data()))) {
|
||||
_err += "Failed to read int array data.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// Handle compressed data
|
||||
if (!ReadCompressedInts((*d)->data(), length)) {
|
||||
_err += "Failed to read compressed int array.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// Handle compressed data
|
||||
if (!ReadCompressedInts(d->data(), length)) {
|
||||
_err += "Failed to read compressed int array.\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4595,7 +4523,7 @@ bool CrateReader::UnpackValueRep(const crate::ValueRep &rep,
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
v = TypedArray<value::float3>(new TypedArrayImpl<value::float3>(const_cast<value::float3*>(reinterpret_cast<const value::float3*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
v = TypedArray<value::float3>(new TypedArray<value::float3>(const_cast<value::float3*>(reinterpret_cast<const value::float3*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
|
||||
// Advance stream reader position
|
||||
if (!_sr->seek_set(current_pos + n * sizeof(value::float3))) {
|
||||
@@ -4684,7 +4612,7 @@ bool CrateReader::UnpackValueRep(const crate::ValueRep &rep,
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
v = TypedArray<value::half3>(new TypedArrayImpl<value::half3>(const_cast<value::half3*>(reinterpret_cast<const value::half3*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
v = TypedArray<value::half3>(new TypedArray<value::half3>(const_cast<value::half3*>(reinterpret_cast<const value::half3*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
|
||||
// Advance stream reader position
|
||||
if (!_sr->seek_set(current_pos + n * sizeof(value::half3))) {
|
||||
@@ -4767,7 +4695,7 @@ bool CrateReader::UnpackValueRep(const crate::ValueRep &rep,
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
v = TypedArray<value::int3>(new TypedArrayImpl<value::int3>(const_cast<value::int3*>(reinterpret_cast<const value::int3*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
v = TypedArray<value::int3>(new TypedArray<value::int3>(const_cast<value::int3*>(reinterpret_cast<const value::int3*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
|
||||
// Advance stream reader position
|
||||
if (!_sr->seek_set(current_pos + n * sizeof(value::int3))) {
|
||||
@@ -4851,7 +4779,7 @@ bool CrateReader::UnpackValueRep(const crate::ValueRep &rep,
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
v = TypedArray<value::double4>(new TypedArrayImpl<value::double4>(const_cast<value::double4*>(reinterpret_cast<const value::double4*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
v = TypedArray<value::double4>(new TypedArray<value::double4>(const_cast<value::double4*>(reinterpret_cast<const value::double4*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
|
||||
// Advance stream reader position
|
||||
if (!_sr->seek_set(current_pos + n * sizeof(value::double4))) {
|
||||
@@ -4934,7 +4862,7 @@ bool CrateReader::UnpackValueRep(const crate::ValueRep &rep,
|
||||
const uint8_t* data_ptr = _sr->data() + current_pos;
|
||||
|
||||
// Create a view over the mmap'd data
|
||||
v = TypedArray<value::float4>(new TypedArrayImpl<value::float4>(const_cast<value::float4*>(reinterpret_cast<const value::float4*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
v = TypedArray<value::float4>(new TypedArray<value::float4>(const_cast<value::float4*>(reinterpret_cast<const value::float4*>(data_ptr)), static_cast<size_t>(n), true), true);
|
||||
|
||||
// Advance stream reader position
|
||||
if (!_sr->seek_set(current_pos + n * sizeof(value::float4))) {
|
||||
|
||||
@@ -419,11 +419,11 @@ std::string try_print_typed_array(const uint8_t* packed_ptr_data) {
|
||||
return ""; // Return empty to indicate failure
|
||||
}
|
||||
|
||||
// Cast to TypedArrayImpl<T>*
|
||||
auto* impl = reinterpret_cast<TypedArrayImpl<T>*>(ptr_bits);
|
||||
// Cast to TypedArray<T>*
|
||||
auto* impl = reinterpret_cast<TypedArray<T>*>(ptr_bits);
|
||||
|
||||
// Create TypedArray with dedup flag to prevent deletion
|
||||
TypedArray<T> typed_array(impl, true);
|
||||
TypedArrayPtr<T> typed_array(impl, true);
|
||||
|
||||
// Create a view to access the data
|
||||
TypedArrayView<const T> view(typed_array);
|
||||
@@ -846,8 +846,8 @@ std::string print_typed_array(const uint8_t* packed_ptr_data) {
|
||||
return ""; // Return empty to indicate failure
|
||||
}
|
||||
|
||||
// Cast to TypedArrayImpl<T>*
|
||||
auto* impl = reinterpret_cast<TypedArrayImpl<T>*>(ptr_bits);
|
||||
// Cast to TypedArray<T>*
|
||||
auto* impl = reinterpret_cast<TypedArray<T>*>(ptr_bits);
|
||||
|
||||
|
||||
if (!impl) {
|
||||
@@ -857,7 +857,7 @@ std::string print_typed_array(const uint8_t* packed_ptr_data) {
|
||||
TUSDZ_LOG_I("impl->size : " << impl->size());
|
||||
|
||||
// Create TypedArray with dedup flag to prevent deletion
|
||||
TypedArray<T> typed_array(impl, true);
|
||||
TypedArrayPtr<T> typed_array(impl, true);
|
||||
|
||||
TUSDZ_LOG_I("typed_array.size = " << typed_array.size());
|
||||
|
||||
@@ -1027,267 +1027,7 @@ void print_value_type(StreamWriter& writer, const uint8_t* data) {
|
||||
|
||||
#endif // #if 0 - Disabled legacy print functions
|
||||
|
||||
// ============================================================================
|
||||
// Active Functions (used by the codebase)
|
||||
// ============================================================================
|
||||
|
||||
// TypedArray - stored as packed pointer (uint64_t)
|
||||
// Attempt to reconstruct TypedArray and print its contents to StreamWriter
|
||||
template<typename T>
|
||||
bool try_print_typed_array_value(StreamWriter& writer, const uint8_t* packed_ptr_data) {
|
||||
uint64_t packed_value;
|
||||
std::memcpy(&packed_value, packed_ptr_data, sizeof(uint64_t));
|
||||
|
||||
//TUSDZ_LOG_I("try_print_typed_array_value: packed_value=0x" << std::hex << packed_value << std::dec);
|
||||
|
||||
// Extract pointer from packed value (lower 48 bits)
|
||||
uint64_t ptr_bits = packed_value & 0x0000FFFFFFFFFFFFULL;
|
||||
|
||||
//TUSDZ_LOG_I("try_print_typed_array_value: after mask ptr_bits=0x" << std::hex << ptr_bits << std::dec);
|
||||
|
||||
// Sign-extend from 48 bits to 64 bits for canonical address
|
||||
if (ptr_bits & (1ULL << 47)) {
|
||||
ptr_bits |= 0xFFFF000000000000ULL;
|
||||
//TUSDZ_LOG_I("try_print_typed_array_value: sign-extended ptr_bits=0x" << std::hex << ptr_bits << std::dec);
|
||||
}
|
||||
|
||||
if (ptr_bits == 0) {
|
||||
return false; // Return false to indicate failure
|
||||
}
|
||||
|
||||
// Cast to TypedArrayImpl<T>*
|
||||
auto* impl = reinterpret_cast<TypedArrayImpl<T>*>(ptr_bits);
|
||||
|
||||
// Check if impl looks valid by examining first few bytes
|
||||
// TypedArrayImpl should have a vtable pointer and size field
|
||||
if (impl == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to inspect the impl structure to understand what we're dealing with
|
||||
//TUSDZ_LOG_I("Inspecting TypedArrayImpl<" << typeid(T).name() << ">* at 0x" << std::hex << ptr_bits << std::dec);
|
||||
|
||||
// Check if impl is valid by trying to access it
|
||||
//TUSDZ_LOG_I("impl->is_view() = " << impl->is_view());
|
||||
//TUSDZ_LOG_I("impl->size() = " << impl->size());
|
||||
//TUSDZ_LOG_I("impl->empty() = " << impl->empty());
|
||||
//TUSDZ_LOG_I("impl->data() = 0x" << std::hex << reinterpret_cast<uintptr_t>(impl->data()) << std::dec);
|
||||
|
||||
// Also check the storage vector size
|
||||
//TUSDZ_LOG_I("impl->storage().size() = " << impl->storage().size());
|
||||
|
||||
// DEBUG: Try to access the data pointer directly to see if it contains valid data
|
||||
const T* data_ptr = impl->data();
|
||||
(void)data_ptr; // Suppress unused warning when debug logging is disabled
|
||||
#if 0 // Disabled debug code
|
||||
if (data_ptr != nullptr) {
|
||||
//TUSDZ_LOG_I("Trying to read first element from data_ptr...");
|
||||
// Try to read first few bytes to see if they look reasonable
|
||||
const uint8_t* byte_ptr = reinterpret_cast<const uint8_t*>(data_ptr);
|
||||
//TUSDZ_LOG_I("First 16 bytes at data_ptr: "
|
||||
// << std::hex
|
||||
// << static_cast<int>(byte_ptr[0]) << " " << static_cast<int>(byte_ptr[1]) << " " << static_cast<int>(byte_ptr[2]) << " " << static_cast<int>(byte_ptr[3]) << " "
|
||||
// << static_cast<int>(byte_ptr[4]) << " " << static_cast<int>(byte_ptr[5]) << " " << static_cast<int>(byte_ptr[6]) << " " << static_cast<int>(byte_ptr[7]) << " "
|
||||
// << static_cast<int>(byte_ptr[8]) << " " << static_cast<int>(byte_ptr[9]) << " " << static_cast<int>(byte_ptr[10]) << " " << static_cast<int>(byte_ptr[11]) << " "
|
||||
// << static_cast<int>(byte_ptr[12]) << " " << static_cast<int>(byte_ptr[13]) << " " << static_cast<int>(byte_ptr[14]) << " " << static_cast<int>(byte_ptr[15])
|
||||
// << std::dec);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create TypedArray with dedup flag to prevent deletion
|
||||
TypedArray<T> typed_array(impl, true);
|
||||
|
||||
// Create a view to access the data
|
||||
TypedArrayView<const T> view(typed_array);
|
||||
|
||||
//TUSDZ_LOG_I("TypedArrayView size: " << view.size());
|
||||
|
||||
// If size is 0, this might not be the right type - return false to try next type
|
||||
if (view.size() == 0) {
|
||||
return false; // Try next type
|
||||
}
|
||||
|
||||
// Always use brackets for arrays (USD spec requires brackets for all arrays)
|
||||
writer.write("[");
|
||||
|
||||
size_t max_elements = view.size();
|
||||
|
||||
for (size_t i = 0; i < max_elements; ++i) {
|
||||
if (i > 0) writer.write(", ");
|
||||
|
||||
// Write the value using operator<< via stringstream
|
||||
std::stringstream ss;
|
||||
ss << view[i];
|
||||
writer.write(ss.str());
|
||||
}
|
||||
|
||||
//if (view.size() > max_elements) {
|
||||
// writer.write(", ... (");
|
||||
// writer.write(static_cast<int>(view.size()));
|
||||
// writer.write(" total)");
|
||||
//}
|
||||
|
||||
writer.write("]");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Print TypedArray when the element type_id is known
|
||||
void print_typed_array_value_by_type_id(StreamWriter& writer, const uint8_t* data, uint32_t type_id) {
|
||||
using namespace value;
|
||||
|
||||
bool success = false;
|
||||
|
||||
// Dispatch to the correct template instantiation based on type_id
|
||||
switch (type_id) {
|
||||
case TYPE_ID_FLOAT:
|
||||
success = try_print_typed_array_value<float>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_DOUBLE:
|
||||
success = try_print_typed_array_value<double>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_INT32:
|
||||
success = try_print_typed_array_value<int32_t>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_FLOAT2:
|
||||
success = try_print_typed_array_value<value::float2>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_FLOAT3:
|
||||
success = try_print_typed_array_value<value::float3>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_FLOAT4:
|
||||
success = try_print_typed_array_value<value::float4>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_DOUBLE2:
|
||||
success = try_print_typed_array_value<value::double2>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_DOUBLE3:
|
||||
success = try_print_typed_array_value<value::double3>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_DOUBLE4:
|
||||
success = try_print_typed_array_value<value::double4>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_TEXCOORD2F:
|
||||
success = try_print_typed_array_value<value::texcoord2f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_TEXCOORD2D:
|
||||
success = try_print_typed_array_value<value::texcoord2d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_TEXCOORD2H:
|
||||
success = try_print_typed_array_value<value::texcoord2h>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_TEXCOORD3F:
|
||||
success = try_print_typed_array_value<value::texcoord3f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_TEXCOORD3D:
|
||||
success = try_print_typed_array_value<value::texcoord3d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_TEXCOORD3H:
|
||||
success = try_print_typed_array_value<value::texcoord3h>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_NORMAL3F:
|
||||
success = try_print_typed_array_value<value::normal3f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_NORMAL3D:
|
||||
success = try_print_typed_array_value<value::normal3d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_NORMAL3H:
|
||||
success = try_print_typed_array_value<value::normal3h>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_POINT3F:
|
||||
success = try_print_typed_array_value<value::point3f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_POINT3D:
|
||||
success = try_print_typed_array_value<value::point3d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_POINT3H:
|
||||
success = try_print_typed_array_value<value::point3h>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_COLOR3F:
|
||||
success = try_print_typed_array_value<value::color3f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_COLOR3D:
|
||||
success = try_print_typed_array_value<value::color3d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_COLOR3H:
|
||||
success = try_print_typed_array_value<value::color3h>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_COLOR4F:
|
||||
success = try_print_typed_array_value<value::color4f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_COLOR4D:
|
||||
success = try_print_typed_array_value<value::color4d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_COLOR4H:
|
||||
success = try_print_typed_array_value<value::color4h>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_VECTOR3F:
|
||||
success = try_print_typed_array_value<value::vector3f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_VECTOR3D:
|
||||
success = try_print_typed_array_value<value::vector3d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_VECTOR3H:
|
||||
success = try_print_typed_array_value<value::vector3h>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_QUATH:
|
||||
success = try_print_typed_array_value<value::quath>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_QUATF:
|
||||
success = try_print_typed_array_value<value::quatf>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_QUATD:
|
||||
success = try_print_typed_array_value<value::quatd>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_MATRIX2F:
|
||||
success = try_print_typed_array_value<value::matrix2f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_MATRIX3F:
|
||||
success = try_print_typed_array_value<value::matrix3f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_MATRIX4F:
|
||||
success = try_print_typed_array_value<value::matrix4f>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_MATRIX2D:
|
||||
success = try_print_typed_array_value<value::matrix2d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_MATRIX3D:
|
||||
success = try_print_typed_array_value<value::matrix3d>(writer, data);
|
||||
break;
|
||||
case TYPE_ID_MATRIX4D:
|
||||
success = try_print_typed_array_value<value::matrix4d>(writer, data);
|
||||
break;
|
||||
default:
|
||||
// Unknown type_id
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// If not successful or unknown type, print generic representation
|
||||
if (!success) {
|
||||
uint64_t packed_value;
|
||||
std::memcpy(&packed_value, data, sizeof(uint64_t));
|
||||
|
||||
uint64_t ptr_bits = packed_value & 0x0000FFFFFFFFFFFFULL;
|
||||
if (ptr_bits & (1ULL << 47)) {
|
||||
ptr_bits |= 0xFFFF000000000000ULL;
|
||||
}
|
||||
|
||||
bool is_dedup = (packed_value & (1ULL << 63)) != 0;
|
||||
|
||||
if (ptr_bits == 0) {
|
||||
writer.write("[]");
|
||||
} else {
|
||||
writer.write("[TypedArray@0x");
|
||||
std::stringstream ss;
|
||||
ss << std::hex << ptr_bits;
|
||||
writer.write(ss.str());
|
||||
if (is_dedup) {
|
||||
writer.write(" (dedup)");
|
||||
}
|
||||
writer.write("]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Old version - commented out since we now use print_typed_array_value_by_type_id
|
||||
// which knows the type_id and doesn't need to guess
|
||||
@@ -1648,649 +1388,6 @@ size_t get_pod_type_size(uint32_t type_id) {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // Currently unused - disabled to use generic path
|
||||
static void pprint_typed_array_timesamples_FLOAT2(StreamWriter& writer, const PODTimeSamples& samples, uint32_t indent) {
|
||||
|
||||
const std::vector<double>& times = samples.get_times();
|
||||
const Buffer<16>& blocked = samples.get_blocked();
|
||||
const Buffer<16>& values = samples.get_values();
|
||||
|
||||
size_t element_size = sizeof(uint64_t);
|
||||
|
||||
if (!samples._offsets.empty()) {
|
||||
// TODO: Check samples._offsets.size() == times.size();
|
||||
}
|
||||
|
||||
{
|
||||
size_t value_offset = 0;
|
||||
for (size_t i = 0; i < times.size(); ++i) {
|
||||
writer.write(pprint::Indent(indent + 1));
|
||||
writer.write(times[i]);
|
||||
writer.write(": ");
|
||||
|
||||
if (blocked[i]) {
|
||||
writer.write("None");
|
||||
} else {
|
||||
|
||||
if (!samples._offsets.empty()) {
|
||||
value_offset = static_cast<size_t>(samples._offsets[i]);
|
||||
}
|
||||
|
||||
// Get pointer to value data for this sample
|
||||
const uint8_t* value_data = values.data() + value_offset;
|
||||
|
||||
// Use correct type based on type_id
|
||||
std::string s;
|
||||
if (samples.type_id() == value::TYPE_ID_TEXCOORD2F) {
|
||||
s = print_typed_array<value::texcoord2f>(value_data);
|
||||
} else {
|
||||
// TYPE_ID_FLOAT2
|
||||
s = print_typed_array<value::float2>(value_data);
|
||||
}
|
||||
writer.write(s);
|
||||
if (samples._offsets.empty()) {
|
||||
value_offset += element_size;
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(","); // USDA allows trailing comma
|
||||
writer.write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // #if 0 - pprint_typed_array_timesamples_FLOAT2
|
||||
|
||||
#if defined(TINYUSDZ_ENABLE_THREAD)
|
||||
// Helper function to print a range of samples to a ChunkedStreamWriter
|
||||
template<typename T>
|
||||
static void pprint_typed_array_timesamples_range(
|
||||
ChunkedStreamWriter<4096>& chunk_writer,
|
||||
const PODTimeSamples& samples,
|
||||
uint32_t indent,
|
||||
size_t start_idx,
|
||||
size_t end_idx,
|
||||
std::map<uint64_t, std::string>& cached_strings) {
|
||||
|
||||
const std::vector<double>& times = samples._times;
|
||||
const Buffer<16>& blocked = samples._blocked;
|
||||
const Buffer<16>& values = samples._values;
|
||||
|
||||
size_t element_size = sizeof(uint64_t);
|
||||
size_t value_offset = 0;
|
||||
|
||||
for (size_t i = start_idx; i < end_idx; ++i) {
|
||||
chunk_writer.write(pprint::Indent(indent + 1));
|
||||
chunk_writer.write(times[i]);
|
||||
chunk_writer.write(": ");
|
||||
|
||||
if (blocked[i]) {
|
||||
chunk_writer.write("None");
|
||||
} else {
|
||||
if (!samples._offsets.empty()) {
|
||||
value_offset = static_cast<size_t>(samples._offsets[i]);
|
||||
} else {
|
||||
// For non-offset tables, calculate based on global index
|
||||
value_offset = i * element_size;
|
||||
}
|
||||
|
||||
// Get pointer to value data for this sample
|
||||
const uint8_t* value_data = values.data() + value_offset;
|
||||
|
||||
// Extract pointer value from packed data
|
||||
uint64_t packed_value;
|
||||
std::memcpy(&packed_value, value_data, sizeof(uint64_t));
|
||||
uint64_t ptr_bits = packed_value & 0x0000FFFFFFFFFFFFULL;
|
||||
|
||||
// Sign-extend from 48 bits to 64 bits
|
||||
if (ptr_bits & (1ULL << 47)) {
|
||||
ptr_bits |= 0xFFFF000000000000ULL;
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
auto it = cached_strings.find(ptr_bits);
|
||||
if (it != cached_strings.end()) {
|
||||
// Reuse cached string
|
||||
chunk_writer.write(it->second);
|
||||
} else {
|
||||
// First occurrence - dereference TypedArray pointer and print
|
||||
// Dereference and print the actual TypedArray contents
|
||||
StreamWriter temp_writer;
|
||||
bool success = try_print_typed_array_value<T>(temp_writer, value_data);
|
||||
if (!success) {
|
||||
temp_writer.write("[TypedArray print failed]");
|
||||
}
|
||||
|
||||
std::string printed = temp_writer.str();
|
||||
chunk_writer.write(printed);
|
||||
|
||||
// Cache the printed string
|
||||
cached_strings[ptr_bits] = printed;
|
||||
}
|
||||
}
|
||||
|
||||
chunk_writer.write(","); // USDA allows trailing comma
|
||||
chunk_writer.write("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Templated efficient printing for typed array timesamples
|
||||
// General template for most types
|
||||
template<typename T>
|
||||
static void pprint_typed_array_timesamples(StreamWriter& writer, const PODTimeSamples& samples, uint32_t indent) {
|
||||
const std::vector<double>& times = samples._times;
|
||||
const Buffer<16>& blocked = samples._blocked;
|
||||
const Buffer<16>& values = samples._values;
|
||||
|
||||
#ifdef TINYUSDZ_ENABLE_THREAD
|
||||
// Use threaded path for large arrays
|
||||
size_t num_samples = times.size();
|
||||
if (num_samples >= g_threaded_print_config.thread_threshold) {
|
||||
unsigned int num_threads = g_threaded_print_config.get_num_threads();
|
||||
size_t samples_per_thread = (num_samples + num_threads - 1) / num_threads;
|
||||
|
||||
// Vector to hold ChunkedStreamWriters for each thread
|
||||
std::vector<ChunkedStreamWriter<4096>> thread_writers(num_threads);
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<std::map<uint64_t, std::string>> thread_caches(num_threads);
|
||||
|
||||
// Launch threads
|
||||
for (unsigned int t = 0; t < num_threads; ++t) {
|
||||
size_t start_idx = t * samples_per_thread;
|
||||
size_t end_idx = std::min(start_idx + samples_per_thread, num_samples);
|
||||
|
||||
if (start_idx >= num_samples) break;
|
||||
|
||||
threads.emplace_back([&, t, start_idx, end_idx]() {
|
||||
pprint_typed_array_timesamples_range<T>(
|
||||
thread_writers[t],
|
||||
samples,
|
||||
indent,
|
||||
start_idx,
|
||||
end_idx,
|
||||
thread_caches[t]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for all threads to complete
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
// Concat all thread results into a single ChunkedStreamWriter
|
||||
ChunkedStreamWriter<4096> final_chunked_writer;
|
||||
for (size_t t = 0; t < thread_writers.size(); ++t) {
|
||||
if (!thread_writers[t].empty()) {
|
||||
final_chunked_writer.concat(std::move(thread_writers[t]));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to string and write to output writer
|
||||
writer.write(final_chunked_writer.str());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Single-threaded path
|
||||
size_t element_size = sizeof(uint64_t);
|
||||
|
||||
// Map to cache printed strings: pointer -> string
|
||||
std::map<uint64_t, std::string> cached_strings;
|
||||
|
||||
size_t value_offset = 0;
|
||||
for (size_t i = 0; i < times.size(); ++i) {
|
||||
writer.write(pprint::Indent(indent + 1));
|
||||
writer.write(times[i]);
|
||||
writer.write(": ");
|
||||
|
||||
if (blocked[i]) {
|
||||
writer.write("None");
|
||||
} else {
|
||||
if (!samples._offsets.empty()) {
|
||||
value_offset = static_cast<size_t>(samples._offsets[i]);
|
||||
}
|
||||
|
||||
// Get pointer to value data for this sample
|
||||
const uint8_t* value_data = values.data() + value_offset;
|
||||
|
||||
// Extract pointer value from packed data
|
||||
uint64_t packed_value;
|
||||
std::memcpy(&packed_value, value_data, sizeof(uint64_t));
|
||||
uint64_t ptr_bits = packed_value & 0x0000FFFFFFFFFFFFULL;
|
||||
|
||||
// Sign-extend from 48 bits to 64 bits
|
||||
if (ptr_bits & (1ULL << 47)) {
|
||||
ptr_bits |= 0xFFFF000000000000ULL;
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
auto it = cached_strings.find(ptr_bits);
|
||||
if (it != cached_strings.end()) {
|
||||
// Reuse cached string
|
||||
writer.write(it->second);
|
||||
} else {
|
||||
// First occurrence - dereference TypedArray pointer and print
|
||||
size_t pos_before = writer.str().size();
|
||||
|
||||
// Dereference and print the actual TypedArray contents
|
||||
bool success = try_print_typed_array_value<T>(writer, value_data);
|
||||
if (!success) {
|
||||
writer.write("[TypedArray print failed]");
|
||||
}
|
||||
|
||||
size_t pos_after = writer.str().size();
|
||||
|
||||
// Cache the printed string
|
||||
std::string printed = writer.str().substr(pos_before, pos_after - pos_before);
|
||||
cached_strings[ptr_bits] = printed;
|
||||
}
|
||||
|
||||
if (samples._offsets.empty()) {
|
||||
value_offset += element_size;
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(","); // USDA allows trailing comma
|
||||
writer.write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Specialization for bool type - stored as uint8_t
|
||||
template<>
|
||||
void pprint_typed_array_timesamples<bool>(StreamWriter& writer, const PODTimeSamples& samples, uint32_t indent) {
|
||||
const std::vector<double>& times = samples._times;
|
||||
const Buffer<16>& blocked = samples._blocked;
|
||||
const Buffer<16>& values = samples._values;
|
||||
|
||||
size_t element_size = sizeof(uint64_t);
|
||||
|
||||
// Map to cache printed strings: pointer -> string
|
||||
std::map<uint64_t, std::string> cached_strings;
|
||||
|
||||
size_t value_offset = 0;
|
||||
for (size_t i = 0; i < times.size(); ++i) {
|
||||
writer.write(pprint::Indent(indent + 1));
|
||||
writer.write(times[i]);
|
||||
writer.write(": ");
|
||||
|
||||
if (blocked[i]) {
|
||||
writer.write("None");
|
||||
} else {
|
||||
if (!samples._offsets.empty()) {
|
||||
value_offset = static_cast<size_t>(samples._offsets[i]);
|
||||
}
|
||||
|
||||
// Get pointer to value data for this sample
|
||||
const uint8_t* value_data = values.data() + value_offset;
|
||||
|
||||
// Extract pointer value from packed data
|
||||
uint64_t packed_value;
|
||||
std::memcpy(&packed_value, value_data, sizeof(uint64_t));
|
||||
uint64_t ptr_bits = packed_value & 0x0000FFFFFFFFFFFFULL;
|
||||
|
||||
// Sign-extend from 48 bits to 64 bits
|
||||
if (ptr_bits & (1ULL << 47)) {
|
||||
ptr_bits |= 0xFFFF000000000000ULL;
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
auto it = cached_strings.find(ptr_bits);
|
||||
if (it != cached_strings.end()) {
|
||||
// Reuse cached string
|
||||
writer.write(it->second);
|
||||
} else {
|
||||
// First occurrence - dereference TypedArray pointer and print
|
||||
// For bool, we treat it as uint8_t storage
|
||||
size_t pos_before = writer.str().size();
|
||||
|
||||
// Try printing as uint8_t (bool's storage type)
|
||||
bool success = try_print_typed_array_value<uint8_t>(writer, value_data);
|
||||
if (!success) {
|
||||
writer.write("[TypedArray<bool> print failed]");
|
||||
}
|
||||
|
||||
size_t pos_after = writer.str().size();
|
||||
|
||||
// Cache the printed string
|
||||
std::string printed = writer.str().substr(pos_before, pos_after - pos_before);
|
||||
cached_strings[ptr_bits] = printed;
|
||||
}
|
||||
|
||||
if (samples._offsets.empty()) {
|
||||
value_offset += element_size;
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(","); // USDA allows trailing comma
|
||||
writer.write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch function to call the correct template based on type_id
|
||||
static bool pprint_typed_array_timesamples_dispatch(StreamWriter& writer, const PODTimeSamples& samples, uint32_t indent) {
|
||||
using namespace value;
|
||||
|
||||
switch (samples.type_id()) {
|
||||
case TYPE_ID_BOOL:
|
||||
pprint_typed_array_timesamples<bool>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_FLOAT:
|
||||
pprint_typed_array_timesamples<float>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_DOUBLE:
|
||||
pprint_typed_array_timesamples<double>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_INT32:
|
||||
pprint_typed_array_timesamples<int32_t>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_FLOAT2:
|
||||
pprint_typed_array_timesamples<value::float2>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_FLOAT3:
|
||||
pprint_typed_array_timesamples<value::float3>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_FLOAT4:
|
||||
pprint_typed_array_timesamples<value::float4>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_DOUBLE2:
|
||||
pprint_typed_array_timesamples<value::double2>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_DOUBLE3:
|
||||
pprint_typed_array_timesamples<value::double3>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_DOUBLE4:
|
||||
pprint_typed_array_timesamples<value::double4>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_TEXCOORD2F:
|
||||
pprint_typed_array_timesamples<value::texcoord2f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_TEXCOORD2D:
|
||||
pprint_typed_array_timesamples<value::texcoord2d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_TEXCOORD2H:
|
||||
pprint_typed_array_timesamples<value::texcoord2h>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_TEXCOORD3F:
|
||||
pprint_typed_array_timesamples<value::texcoord3f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_TEXCOORD3D:
|
||||
pprint_typed_array_timesamples<value::texcoord3d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_TEXCOORD3H:
|
||||
pprint_typed_array_timesamples<value::texcoord3h>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_NORMAL3F:
|
||||
pprint_typed_array_timesamples<value::normal3f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_NORMAL3D:
|
||||
pprint_typed_array_timesamples<value::normal3d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_NORMAL3H:
|
||||
pprint_typed_array_timesamples<value::normal3h>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_POINT3F:
|
||||
pprint_typed_array_timesamples<value::point3f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_POINT3D:
|
||||
pprint_typed_array_timesamples<value::point3d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_POINT3H:
|
||||
pprint_typed_array_timesamples<value::point3h>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_COLOR3F:
|
||||
pprint_typed_array_timesamples<value::color3f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_COLOR3D:
|
||||
pprint_typed_array_timesamples<value::color3d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_COLOR3H:
|
||||
pprint_typed_array_timesamples<value::color3h>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_COLOR4F:
|
||||
pprint_typed_array_timesamples<value::color4f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_COLOR4D:
|
||||
pprint_typed_array_timesamples<value::color4d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_COLOR4H:
|
||||
pprint_typed_array_timesamples<value::color4h>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_VECTOR3F:
|
||||
pprint_typed_array_timesamples<value::vector3f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_VECTOR3D:
|
||||
pprint_typed_array_timesamples<value::vector3d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_VECTOR3H:
|
||||
pprint_typed_array_timesamples<value::vector3h>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_QUATH:
|
||||
pprint_typed_array_timesamples<value::quath>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_QUATF:
|
||||
pprint_typed_array_timesamples<value::quatf>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_QUATD:
|
||||
pprint_typed_array_timesamples<value::quatd>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_MATRIX2F:
|
||||
pprint_typed_array_timesamples<value::matrix2f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_MATRIX3F:
|
||||
pprint_typed_array_timesamples<value::matrix3f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_MATRIX4F:
|
||||
pprint_typed_array_timesamples<value::matrix4f>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_MATRIX2D:
|
||||
pprint_typed_array_timesamples<value::matrix2d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_MATRIX3D:
|
||||
pprint_typed_array_timesamples<value::matrix3d>(writer, samples, indent);
|
||||
return true;
|
||||
case TYPE_ID_MATRIX4D:
|
||||
pprint_typed_array_timesamples<value::matrix4d>(writer, samples, indent);
|
||||
return true;
|
||||
default:
|
||||
// Type not supported by optimized path
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void pprint_pod_timesamples(StreamWriter& writer, const PODTimeSamples& samples, uint32_t indent) {
|
||||
// Write opening brace
|
||||
writer.write("{\n");
|
||||
|
||||
if (samples.empty()) {
|
||||
writer.write(pprint::Indent(indent));
|
||||
writer.write("}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get element size for this type
|
||||
size_t element_size = samples._is_typed_array ? sizeof(uint64_t) : get_pod_type_size(samples.type_id());
|
||||
if (element_size == 0) {
|
||||
writer.write(pprint::Indent(indent + 1));
|
||||
writer.write("[Error: Unknown type_id ");
|
||||
writer.write(samples.type_id());
|
||||
writer.write("]\n");
|
||||
writer.write(pprint::Indent(indent));
|
||||
writer.write("}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure samples are updated (sorted)
|
||||
if (samples._dirty) {
|
||||
samples.update();
|
||||
}
|
||||
|
||||
bool printed_array = false;
|
||||
if (samples._is_typed_array) {
|
||||
// Route to optimized template-based path
|
||||
printed_array = pprint_typed_array_timesamples_dispatch(writer, samples, indent);
|
||||
}
|
||||
|
||||
if (!printed_array) {
|
||||
// Fallback
|
||||
const std::vector<double>& times = samples._times;
|
||||
const Buffer<16>& blocked = samples._blocked;
|
||||
const Buffer<16>& values = samples._values;
|
||||
|
||||
// Check if using offset table (new optimized storage)
|
||||
if (!samples._offsets.empty()) {
|
||||
// Verify offset table is correct size
|
||||
if (samples._offsets.size() != times.size()) {
|
||||
writer.write(pprint::Indent(indent + 1));
|
||||
writer.write("[Error: Offset table size mismatch: offsets=");
|
||||
writer.write(static_cast<int>(samples._offsets.size()));
|
||||
writer.write(" times=");
|
||||
writer.write(static_cast<int>(times.size()));
|
||||
writer.write("]\n");
|
||||
writer.write(pprint::Indent(indent));
|
||||
writer.write("}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimization for TypedArray: cache printed strings by pointer value
|
||||
if (samples._is_typed_array) {
|
||||
// Map to cache printed strings: pointer -> string
|
||||
std::map<uint64_t, std::string> cached_strings;
|
||||
|
||||
// Using offset table - blocked values don't consume space
|
||||
for (size_t i = 0; i < times.size(); ++i) {
|
||||
writer.write(pprint::Indent(indent + 1));
|
||||
writer.write(times[i]);
|
||||
writer.write(": ");
|
||||
|
||||
if (blocked[i] || samples._offsets[i] == SIZE_MAX) {
|
||||
writer.write("None");
|
||||
} else {
|
||||
// Get pointer to value data using offset
|
||||
const uint8_t* value_data = values.data() + samples._offsets[i];
|
||||
|
||||
//TUSDZ_LOG_I("pprint_pod_timesamples: i=" << i << " offset=" << samples._offsets[i] << " value_data=0x" << std::hex << reinterpret_cast<uintptr_t>(value_data) << std::dec);
|
||||
|
||||
// Extract pointer value from packed data
|
||||
uint64_t packed_value;
|
||||
std::memcpy(&packed_value, value_data, sizeof(uint64_t));
|
||||
//TUSDZ_LOG_I("pprint_pod_timesamples: packed_value=0x" << std::hex << packed_value << std::dec);
|
||||
uint64_t ptr_bits = packed_value & 0x0000FFFFFFFFFFFFULL;
|
||||
|
||||
// Sign-extend from 48 bits to 64 bits
|
||||
if (ptr_bits & (1ULL << 47)) {
|
||||
ptr_bits |= 0xFFFF000000000000ULL;
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
auto it = cached_strings.find(ptr_bits);
|
||||
if (it != cached_strings.end()) {
|
||||
// Reuse cached string
|
||||
writer.write(it->second);
|
||||
} else {
|
||||
// First occurrence - dereference TypedArray pointer and print
|
||||
size_t pos_before = writer.str().size();
|
||||
|
||||
// Dereference and print the actual TypedArray contents using known type_id
|
||||
print_typed_array_value_by_type_id(writer, value_data, samples.type_id());
|
||||
|
||||
size_t pos_after = writer.str().size();
|
||||
|
||||
// Cache the printed string
|
||||
std::string printed = writer.str().substr(pos_before, pos_after - pos_before);
|
||||
cached_strings[ptr_bits] = printed;
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(","); // USDA allows trailing comma
|
||||
writer.write("\n");
|
||||
}
|
||||
} else {
|
||||
// Non-TypedArray path: use regular printing
|
||||
for (size_t i = 0; i < times.size(); ++i) {
|
||||
writer.write(pprint::Indent(indent + 1));
|
||||
writer.write(times[i]);
|
||||
writer.write(": ");
|
||||
|
||||
if (blocked[i] || samples._offsets[i] == SIZE_MAX) {
|
||||
writer.write("None");
|
||||
} else {
|
||||
// Resolve offset to handle dedup and get resolved sample index
|
||||
size_t byte_offset = 0;
|
||||
size_t resolved_idx = i;
|
||||
bool is_array = false;
|
||||
if (!samples.resolve_offset(i, &byte_offset, &is_array, nullptr, 100, &resolved_idx)) {
|
||||
writer.write("[Error: Failed to resolve offset]");
|
||||
} else {
|
||||
// Get pointer to value data using resolved offset
|
||||
const uint8_t* value_data = values.data() + byte_offset;
|
||||
|
||||
// Check if this sample is an array
|
||||
is_array = is_array || samples._is_stl_array;
|
||||
|
||||
if (is_array) {
|
||||
// Get per-sample array count (with fallback to global _array_size)
|
||||
size_t array_count = (resolved_idx < samples._array_counts.size()) ?
|
||||
samples._array_counts[resolved_idx] : samples._array_size;
|
||||
// Print all elements in the array
|
||||
pprint_pod_array_by_type(writer, value_data, samples.type_id(), array_count);
|
||||
} else {
|
||||
// Print single value
|
||||
pprint_pod_value_by_type(writer, value_data, samples.type_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(","); // USDA allows trailing comma
|
||||
writer.write("\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Legacy storage - blocked values still consume space but are skipped
|
||||
size_t value_offset = 0;
|
||||
for (size_t i = 0; i < times.size(); ++i) {
|
||||
writer.write(pprint::Indent(indent + 1));
|
||||
writer.write(times[i]);
|
||||
writer.write(": ");
|
||||
|
||||
if (blocked[i]) {
|
||||
writer.write("None");
|
||||
} else {
|
||||
// Get pointer to value data for this sample
|
||||
const uint8_t* value_data = values.data() + value_offset;
|
||||
|
||||
// Check if this is an array type
|
||||
bool is_array = samples._is_stl_array;
|
||||
|
||||
if (is_array) {
|
||||
// Get per-sample array count (with fallback to global _array_size)
|
||||
size_t array_count = (i < samples._array_counts.size()) ?
|
||||
samples._array_counts[i] : samples._array_size;
|
||||
// Print all elements in the array
|
||||
pprint_pod_array_by_type(writer, value_data, samples.type_id(), array_count);
|
||||
} else {
|
||||
// Print single value
|
||||
pprint_pod_value_by_type(writer, value_data, samples.type_id());
|
||||
}
|
||||
value_offset += element_size;
|
||||
}
|
||||
|
||||
writer.write(","); // USDA allows trailing comma
|
||||
writer.write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(pprint::Indent(indent));
|
||||
writer.write("}");
|
||||
}
|
||||
|
||||
std::string pprint_pod_timesamples(const PODTimeSamples& samples, uint32_t indent) {
|
||||
// Use StreamWriter internally for efficiency
|
||||
StreamWriter writer;
|
||||
pprint_pod_timesamples(writer, samples, indent);
|
||||
return writer.str();
|
||||
}
|
||||
|
||||
void pprint_timesamples(StreamWriter& writer, const value::TimeSamples& samples, uint32_t indent) {
|
||||
// Write opening brace
|
||||
@@ -2302,8 +1399,8 @@ void pprint_timesamples(StreamWriter& writer, const value::TimeSamples& samples,
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if using POD storage
|
||||
if (samples.is_using_pod()) {
|
||||
// Check if using unified storage (_times non-empty) vs legacy Sample-based storage
|
||||
if (!samples.get_times().empty()) {
|
||||
|
||||
// Phase 3: Access unified storage directly from TimeSamples
|
||||
// Note: TypedArray is no longer supported in Phase 3, so we skip that path
|
||||
@@ -2352,14 +1449,14 @@ void pprint_timesamples(StreamWriter& writer, const value::TimeSamples& samples,
|
||||
// Resolve offset (may be encoded with dedup/array flags) and get resolved index
|
||||
size_t byte_offset;
|
||||
size_t resolved_idx = i;
|
||||
if (!PODTimeSamples::resolve_offset_static(offsets, i, &byte_offset, nullptr, nullptr, 100, &resolved_idx)) {
|
||||
if (!value::TimeSamples::resolve_offset_static(offsets, i, &byte_offset, nullptr, nullptr, 100, &resolved_idx)) {
|
||||
writer.write("/* ERROR: failed to resolve offset */");
|
||||
} else {
|
||||
// Get pointer to value data using resolved byte offset
|
||||
const uint8_t* value_ptr = values.data() + byte_offset;
|
||||
|
||||
// Check if this sample is an array (check array flag in offset)
|
||||
bool is_array = samples.is_stl_array() || (offsets[i] & PODTimeSamples::OFFSET_ARRAY_FLAG);
|
||||
bool is_array = samples.is_stl_array() || (offsets[i] & value::TimeSamples::OFFSET_ARRAY_FLAG);
|
||||
|
||||
if (is_array) {
|
||||
// Get per-sample array count (with fallback to global array_size)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
///
|
||||
/// @file timesamples-pprint.hh
|
||||
/// @brief Pretty printing functions for PODTimeSamples
|
||||
/// @brief Pretty printing functions for TimeSamples
|
||||
///
|
||||
|
||||
#pragma once
|
||||
@@ -14,7 +14,6 @@
|
||||
namespace tinyusdz {
|
||||
|
||||
// Forward declarations
|
||||
struct PODTimeSamples;
|
||||
class StreamWriter;
|
||||
|
||||
namespace value {
|
||||
@@ -23,27 +22,6 @@ class Value;
|
||||
struct TimeSamples;
|
||||
} // namespace value
|
||||
|
||||
///
|
||||
/// Pretty print PODTimeSamples with indentation support
|
||||
///
|
||||
/// @param samples PODTimeSamples to print
|
||||
/// @param indent Indentation level (number of spaces)
|
||||
/// @return String representation of the time samples
|
||||
///
|
||||
std::string pprint_pod_timesamples(const PODTimeSamples& samples,
|
||||
uint32_t indent = 0);
|
||||
|
||||
///
|
||||
/// Pretty print PODTimeSamples to a StreamWriter
|
||||
///
|
||||
/// @param writer StreamWriter to write to
|
||||
/// @param samples PODTimeSamples to print
|
||||
/// @param indent Indentation level (number of spaces)
|
||||
///
|
||||
void pprint_pod_timesamples(StreamWriter& writer,
|
||||
const PODTimeSamples& samples,
|
||||
uint32_t indent = 0);
|
||||
|
||||
///
|
||||
/// Pretty print a single POD value based on type_id
|
||||
///
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1719
src/timesamples.hh
1719
src/timesamples.hh
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "typed-array.hh"
|
||||
|
||||
// Implementation file for TypedArrayImpl - since it's header-only template class,
|
||||
// Implementation file for TypedArray - since it's header-only template class,
|
||||
// most functionality is implemented in the header file.
|
||||
// This file can be used for explicit template instantiations if needed.
|
||||
|
||||
@@ -18,17 +18,7 @@ namespace tinyusdz {
|
||||
// Explicit template instantiations for commonly used types
|
||||
// This can help reduce compilation time by pre-instantiating frequently used types
|
||||
|
||||
// TypedArrayImpl instantiations
|
||||
template class TypedArrayImpl<uint8_t>;
|
||||
template class TypedArrayImpl<uint16_t>;
|
||||
template class TypedArrayImpl<uint32_t>;
|
||||
template class TypedArrayImpl<int8_t>;
|
||||
template class TypedArrayImpl<int16_t>;
|
||||
template class TypedArrayImpl<int32_t>;
|
||||
template class TypedArrayImpl<float>;
|
||||
template class TypedArrayImpl<double>;
|
||||
|
||||
// TypedArray (packed pointer) instantiations
|
||||
// TypedArray (storage class) instantiations
|
||||
template class TypedArray<uint8_t>;
|
||||
template class TypedArray<uint16_t>;
|
||||
template class TypedArray<uint32_t>;
|
||||
@@ -38,6 +28,16 @@ template class TypedArray<int32_t>;
|
||||
template class TypedArray<float>;
|
||||
template class TypedArray<double>;
|
||||
|
||||
// TypedArrayPtr (packed pointer wrapper) instantiations
|
||||
template class TypedArrayPtr<uint8_t>;
|
||||
template class TypedArrayPtr<uint16_t>;
|
||||
template class TypedArrayPtr<uint32_t>;
|
||||
template class TypedArrayPtr<int8_t>;
|
||||
template class TypedArrayPtr<int16_t>;
|
||||
template class TypedArrayPtr<int32_t>;
|
||||
template class TypedArrayPtr<float>;
|
||||
template class TypedArrayPtr<double>;
|
||||
|
||||
// TypedArrayView instantiations
|
||||
template class TypedArrayView<uint8_t>;
|
||||
template class TypedArrayView<const uint8_t>;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
namespace tinyusdz {
|
||||
|
||||
template <typename T>
|
||||
class TypedArrayImpl {
|
||||
class TypedArray {
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
@@ -36,22 +36,20 @@ class TypedArrayImpl {
|
||||
using const_iterator = const T*;
|
||||
|
||||
// Default constructor
|
||||
TypedArrayImpl() = default;
|
||||
TypedArray() = default;
|
||||
|
||||
// Constructor with size
|
||||
explicit TypedArrayImpl(size_type count) {
|
||||
_is_view = false;
|
||||
explicit TypedArray(size_type count) {
|
||||
resize(count);
|
||||
}
|
||||
|
||||
// Constructor with size and default value
|
||||
TypedArrayImpl(size_type count, const T& value) {
|
||||
_is_view = false;
|
||||
TypedArray(size_type count, const T& value) {
|
||||
resize(count, value);
|
||||
}
|
||||
|
||||
// Constructor from initializer list
|
||||
TypedArrayImpl(std::initializer_list<T> init) {
|
||||
TypedArray(std::initializer_list<T> init) {
|
||||
reserve(init.size());
|
||||
for (const auto& item : init) {
|
||||
push_back(item);
|
||||
@@ -59,147 +57,66 @@ class TypedArrayImpl {
|
||||
}
|
||||
|
||||
// Constructor from existing data (copies data)
|
||||
TypedArrayImpl(const T* data, size_type count) {
|
||||
TypedArray(const T* data, size_type count) {
|
||||
if (data && count > 0) {
|
||||
_storage.resize(count * sizeof(T));
|
||||
std::memcpy(_storage.data(), data, count * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
// View constructor - creates a non-owning view over external data
|
||||
// When is_view=true, no allocation occurs, just stores pointer and size
|
||||
TypedArrayImpl(T* data, size_type count, bool is_view) {
|
||||
if (is_view && data && count > 0) {
|
||||
_view_ptr = data;
|
||||
_view_size = count;
|
||||
_is_view = true;
|
||||
} else if (!is_view && data && count > 0) {
|
||||
// Non-view mode: copy data
|
||||
_storage.resize(count * sizeof(T));
|
||||
std::memcpy(_storage.data(), data, count * sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
TypedArrayImpl(const TypedArrayImpl& other) {
|
||||
if (other._is_view) {
|
||||
// Copy view properties
|
||||
_view_ptr = other._view_ptr;
|
||||
_view_size = other._view_size;
|
||||
_is_view = true;
|
||||
} else {
|
||||
// Copy storage
|
||||
_storage = other._storage;
|
||||
_is_view = false;
|
||||
}
|
||||
TypedArray(const TypedArray& other)
|
||||
: _storage(other._storage) {
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
TypedArrayImpl(TypedArrayImpl&& other) noexcept {
|
||||
//DCOUT("TypedArrayImpl move ctor: this=" << std::hex << this << " from other=" << &other
|
||||
// << " other._is_view=" << other._is_view << " other.size()=" << std::dec << other.size());
|
||||
if (other._is_view) {
|
||||
_view_ptr = other._view_ptr;
|
||||
_view_size = other._view_size;
|
||||
_is_view = true;
|
||||
other._view_ptr = nullptr;
|
||||
other._view_size = 0;
|
||||
} else {
|
||||
_storage = std::move(other._storage);
|
||||
_is_view = false;
|
||||
}
|
||||
DCOUT("TypedArrayImpl move ctor done: this.size()=" << size() << " other.size()=" << other.size());
|
||||
TypedArray(TypedArray&& other) noexcept
|
||||
: _storage(std::move(other._storage)) {
|
||||
}
|
||||
|
||||
// Copy assignment
|
||||
TypedArrayImpl& operator=(const TypedArrayImpl& other) {
|
||||
TypedArray& operator=(const TypedArray& other) {
|
||||
if (this != &other) {
|
||||
if (other._is_view) {
|
||||
_storage.clear();
|
||||
_view_ptr = other._view_ptr;
|
||||
_view_size = other._view_size;
|
||||
_is_view = true;
|
||||
} else {
|
||||
_view_ptr = nullptr;
|
||||
_view_size = 0;
|
||||
_storage = other._storage;
|
||||
_is_view = false;
|
||||
}
|
||||
_storage = other._storage;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move assignment
|
||||
TypedArrayImpl& operator=(TypedArrayImpl&& other) noexcept {
|
||||
//DCOUT("TypedArrayImpl move assign: this=" << std::hex << this << " from other=" << &other
|
||||
// << " this.size()=" << std::dec << size() << " other.size()=" << other.size());
|
||||
TypedArray& operator=(TypedArray&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (other._is_view) {
|
||||
_storage.clear();
|
||||
_view_ptr = other._view_ptr;
|
||||
_view_size = other._view_size;
|
||||
_is_view = true;
|
||||
other._view_ptr = nullptr;
|
||||
other._view_size = 0;
|
||||
} else {
|
||||
_view_ptr = nullptr;
|
||||
_view_size = 0;
|
||||
_storage = std::move(other._storage);
|
||||
_is_view = false;
|
||||
}
|
||||
_storage = std::move(other._storage);
|
||||
}
|
||||
DCOUT("TypedArrayImpl move assign done: this.size()=" << size() << " other.size()=" << other.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~TypedArrayImpl() {
|
||||
//DCOUT("TypedArrayImpl dtor: this=" << std::hex << this << " _is_view=" << _is_view << " size()=" << std::dec << size());
|
||||
if (_is_view) {
|
||||
// no free
|
||||
} else {
|
||||
_storage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is a view (non-owning)
|
||||
bool is_view() const noexcept { return _is_view; }
|
||||
|
||||
// Create a view from this array
|
||||
TypedArrayImpl create_view() const {
|
||||
return TypedArrayImpl(const_cast<T*>(data()), size(), true);
|
||||
}
|
||||
|
||||
// Static helper to create a view
|
||||
static TypedArrayImpl make_view(T* data, size_type count) {
|
||||
return TypedArrayImpl(data, count, true);
|
||||
}
|
||||
~TypedArray() = default;
|
||||
|
||||
// Size operations
|
||||
size_type size() const noexcept {
|
||||
return _is_view ? _view_size : (_storage.size() / sizeof(T));
|
||||
return _storage.size() / sizeof(T);
|
||||
}
|
||||
|
||||
size_type capacity() const noexcept {
|
||||
return _is_view ? _view_size : (_storage.capacity() / sizeof(T));
|
||||
return _storage.capacity() / sizeof(T);
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return _is_view ? (_view_size == 0) : _storage.empty();
|
||||
return _storage.empty();
|
||||
}
|
||||
|
||||
size_type max_size() const noexcept {
|
||||
return _is_view ? _view_size : (_storage.max_size() / sizeof(T));
|
||||
return _storage.max_size() / sizeof(T);
|
||||
}
|
||||
|
||||
// Data access
|
||||
pointer data() noexcept {
|
||||
return _is_view ? _view_ptr : reinterpret_cast<pointer>(_storage.data());
|
||||
return reinterpret_cast<pointer>(_storage.data());
|
||||
}
|
||||
|
||||
const_pointer data() const noexcept {
|
||||
return _is_view ? const_cast<const_pointer>(_view_ptr)
|
||||
: reinterpret_cast<const_pointer>(_storage.data());
|
||||
return reinterpret_cast<const_pointer>(_storage.data());
|
||||
}
|
||||
|
||||
// Element access
|
||||
@@ -303,31 +220,14 @@ class TypedArrayImpl {
|
||||
|
||||
// Modifiers
|
||||
void clear() noexcept {
|
||||
if (_is_view) {
|
||||
// For view mode, just reset the view
|
||||
_view_ptr = nullptr;
|
||||
_view_size = 0;
|
||||
} else {
|
||||
_storage.clear();
|
||||
}
|
||||
_storage.clear();
|
||||
}
|
||||
|
||||
bool resize(size_type count) {
|
||||
if (_is_view) {
|
||||
// Cannot resize a view - this would require allocation
|
||||
// Could throw an exception or assert, but for now just return
|
||||
// assert(!_is_view && "Cannot resize a TypedArray view");
|
||||
return false;
|
||||
}
|
||||
void resize(size_type count) {
|
||||
_storage.resize(count * sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resize(size_type count, const T& value) {
|
||||
if (_is_view) {
|
||||
// assert(!_is_view && "Cannot resize a TypedArray view");
|
||||
return false;
|
||||
}
|
||||
void resize(size_type count, const T& value) {
|
||||
size_type old_size = size();
|
||||
_storage.resize(count * sizeof(T));
|
||||
|
||||
@@ -335,71 +235,46 @@ class TypedArrayImpl {
|
||||
for (size_type i = old_size; i < count; ++i) {
|
||||
new (data() + i) T(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool reserve(size_type new_capacity) {
|
||||
if (_is_view) {
|
||||
// assert(!_is_view && "Cannot reserve capacity for a TypedArray view");
|
||||
return false;
|
||||
}
|
||||
void reserve(size_type new_capacity) {
|
||||
_storage.reserve(new_capacity * sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
void shrink_to_fit() {
|
||||
if (!_is_view) {
|
||||
_storage.shrink_to_fit();
|
||||
}
|
||||
_storage.shrink_to_fit();
|
||||
}
|
||||
|
||||
bool push_back(const T& value) {
|
||||
if (_is_view) {
|
||||
// assert(!_is_view && "Cannot push_back to a TypedArray view");
|
||||
return false;
|
||||
}
|
||||
void push_back(const T& value) {
|
||||
size_type old_size = size();
|
||||
resize(old_size + 1);
|
||||
data()[old_size] = value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool push_back(T&& value) {
|
||||
if (_is_view) {
|
||||
// assert(!_is_view && "Cannot push_back to a TypedArray view");
|
||||
return false;
|
||||
}
|
||||
void push_back(T&& value) {
|
||||
size_type old_size = size();
|
||||
resize(old_size + 1);
|
||||
data()[old_size] = std::move(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop_back() {
|
||||
if (_is_view) {
|
||||
return false;
|
||||
}
|
||||
void pop_back() {
|
||||
if (!empty()) {
|
||||
resize(size() - 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// In-place transform function
|
||||
// Transforms from current type T to new type N
|
||||
// Requirement: sizeof(T) >= sizeof(N) for in-place operation
|
||||
template <typename N, typename Func>
|
||||
TypedArrayImpl<N> transform(Func func) const {
|
||||
TypedArray<N> transform(Func func) const {
|
||||
static_assert(sizeof(T) >= sizeof(N),
|
||||
"transform: source type size must be >= destination type "
|
||||
"size for in-place operation");
|
||||
static_assert(std::is_trivially_copyable<N>::value,
|
||||
"transform: destination type must be trivially copyable");
|
||||
|
||||
TypedArrayImpl<N> result;
|
||||
TypedArray<N> result;
|
||||
if (empty()) {
|
||||
return result;
|
||||
}
|
||||
@@ -427,7 +302,7 @@ class TypedArrayImpl {
|
||||
|
||||
// Specialized transform for 1:1 type conversion (e.g., int to float)
|
||||
template <typename N, typename Func>
|
||||
TypedArrayImpl<N> transform_1to1(Func func) const {
|
||||
TypedArray<N> transform_1to1(Func func) const {
|
||||
static_assert(
|
||||
sizeof(T) >= sizeof(N),
|
||||
"transform_1to1: source type size must be >= destination type size");
|
||||
@@ -435,7 +310,7 @@ class TypedArrayImpl {
|
||||
std::is_trivially_copyable<N>::value,
|
||||
"transform_1to1: destination type must be trivially copyable");
|
||||
|
||||
TypedArrayImpl<N> result;
|
||||
TypedArray<N> result;
|
||||
if (empty()) {
|
||||
return result;
|
||||
}
|
||||
@@ -452,12 +327,12 @@ class TypedArrayImpl {
|
||||
// Enhanced transform supporting sizeof(srcTy) <= sizeof(dstTy) with
|
||||
// controlled buffer growth
|
||||
template <typename N, typename Func>
|
||||
TypedArrayImpl<N> transform_expand(Func func) const {
|
||||
TypedArray<N> transform_expand(Func func) const {
|
||||
static_assert(
|
||||
std::is_trivially_copyable<N>::value,
|
||||
"transform_expand: destination type must be trivially copyable");
|
||||
|
||||
TypedArrayImpl<N> result;
|
||||
TypedArray<N> result;
|
||||
if (empty()) {
|
||||
return result;
|
||||
}
|
||||
@@ -474,13 +349,13 @@ class TypedArrayImpl {
|
||||
// In-place transform with expansion (modifies current array)
|
||||
// Only works when sizeof(T) <= sizeof(N) and we have sufficient capacity
|
||||
template <typename N, typename Func>
|
||||
TypedArrayImpl<N> transform_inplace_expand(Func func) {
|
||||
TypedArray<N> transform_inplace_expand(Func func) {
|
||||
static_assert(std::is_trivially_copyable<N>::value,
|
||||
"transform_inplace_expand: destination type must be "
|
||||
"trivially copyable");
|
||||
|
||||
if (empty()) {
|
||||
TypedArrayImpl<N> result;
|
||||
TypedArray<N> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -505,8 +380,8 @@ class TypedArrayImpl {
|
||||
func(src_data[src_idx], dst_data[dst_idx]);
|
||||
}
|
||||
|
||||
// Return TypedArrayImpl<N> that shares the same storage
|
||||
TypedArrayImpl<N> result;
|
||||
// Return TypedArray<N> that shares the same storage
|
||||
TypedArray<N> result;
|
||||
result._storage = std::move(_storage);
|
||||
_storage.clear(); // Current array is now empty
|
||||
return result;
|
||||
@@ -518,13 +393,13 @@ class TypedArrayImpl {
|
||||
|
||||
// Transform with controlled growth - specify maximum buffer growth factor
|
||||
template <typename N, typename Func>
|
||||
TypedArrayImpl<N> transform_with_limit(Func func,
|
||||
TypedArray<N> transform_with_limit(Func func,
|
||||
double max_growth_factor = 2.0) const {
|
||||
static_assert(
|
||||
std::is_trivially_copyable<N>::value,
|
||||
"transform_with_limit: destination type must be trivially copyable");
|
||||
|
||||
TypedArrayImpl<N> result;
|
||||
TypedArray<N> result;
|
||||
if (empty()) {
|
||||
return result;
|
||||
}
|
||||
@@ -557,33 +432,25 @@ class TypedArrayImpl {
|
||||
const std::vector<uint8_t>& storage() const noexcept { return _storage; }
|
||||
|
||||
// Swap
|
||||
void swap(TypedArrayImpl& other) noexcept {
|
||||
if (_is_view || other._is_view) {
|
||||
// Swap all members including view state
|
||||
std::swap(_storage, other._storage);
|
||||
std::swap(_view_ptr, other._view_ptr);
|
||||
std::swap(_view_size, other._view_size);
|
||||
std::swap(_is_view, other._is_view);
|
||||
} else {
|
||||
_storage.swap(other._storage);
|
||||
}
|
||||
void swap(TypedArray& other) noexcept {
|
||||
_storage.swap(other._storage);
|
||||
}
|
||||
|
||||
// Comparison operators
|
||||
bool operator==(const TypedArrayImpl& other) const {
|
||||
bool operator==(const TypedArray& other) const {
|
||||
if (size() != other.size()) return false;
|
||||
if (size() == 0) return true;
|
||||
return std::memcmp(data(), other.data(), size() * sizeof(T)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const TypedArrayImpl& other) const {
|
||||
bool operator!=(const TypedArray& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// Hash function for use with unordered_map
|
||||
// Hashes ALL elements for correctness
|
||||
struct Hash {
|
||||
size_t operator()(const TypedArrayImpl<T>& arr) const {
|
||||
size_t operator()(const TypedArray<T>& arr) const {
|
||||
size_t hash = std::hash<size_t>{}(arr.size());
|
||||
|
||||
// Hash all elements for complete hash coverage
|
||||
@@ -612,15 +479,12 @@ class TypedArrayImpl {
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> _storage;
|
||||
T* _view_ptr = nullptr; // Pointer to external data when in view mode
|
||||
size_type _view_size = 0; // Size of view in elements
|
||||
bool _is_view = false; // Flag indicating view mode
|
||||
|
||||
// Helper method implementations for C++14 compatibility (instead of if
|
||||
// constexpr)
|
||||
template <typename N, typename Func>
|
||||
TypedArrayImpl<N> transform_expand_impl(
|
||||
Func func, size_type src_count, TypedArrayImpl<N>& result,
|
||||
TypedArray<N> transform_expand_impl(
|
||||
Func func, size_type src_count, TypedArray<N>& result,
|
||||
std::true_type /* sizeof(T) >= sizeof(N) */) const {
|
||||
// Shrinking case - can use in-place approach
|
||||
size_type dst_count = (src_count * sizeof(T)) / sizeof(N);
|
||||
@@ -639,8 +503,8 @@ class TypedArrayImpl {
|
||||
}
|
||||
|
||||
template <typename N, typename Func>
|
||||
TypedArrayImpl<N> transform_expand_impl(
|
||||
Func func, size_type src_count, TypedArrayImpl<N>& result,
|
||||
TypedArray<N> transform_expand_impl(
|
||||
Func func, size_type src_count, TypedArray<N>& result,
|
||||
std::false_type /* sizeof(T) < sizeof(N) */) const {
|
||||
// Expanding case - requires buffer growth
|
||||
// Buffer grows exactly to needed size: num_items * sizeof(dstTy)
|
||||
@@ -654,12 +518,12 @@ class TypedArrayImpl {
|
||||
};
|
||||
|
||||
///
|
||||
/// TypedArray: Optimized 64-bit storage for TypedArrayImpl<T> pointers
|
||||
/// TypedArrayPtr: Optimized 64-bit storage for TypedArray<T> pointers
|
||||
///
|
||||
/// Memory layout:
|
||||
/// Bit 63 (MSB): dedup/mmap flag (1 = shared/mmap, don't delete; 0 =
|
||||
/// owned, delete on destruction) Bits 0-47: 48-bit pointer to
|
||||
/// TypedArrayImpl<T> object (sufficient for x86-64 canonical addresses) Bits
|
||||
/// TypedArray<T> object (sufficient for x86-64 canonical addresses) Bits
|
||||
/// 48-62: Reserved/unused (15 bits available for future use)
|
||||
///
|
||||
/// The 48-bit pointer is sufficient because:
|
||||
@@ -674,36 +538,36 @@ class TypedArrayImpl {
|
||||
/// on destruction
|
||||
///
|
||||
template <typename T>
|
||||
class TypedArray {
|
||||
class TypedArrayPtr {
|
||||
public:
|
||||
// Default constructor - creates owned array with size 0
|
||||
TypedArray() noexcept : _packed_data(0) {
|
||||
reset(new TypedArrayImpl<T>(0), false);
|
||||
TypedArrayPtr() noexcept : _packed_data(0) {
|
||||
reset(new TypedArray<T>(0), false);
|
||||
}
|
||||
|
||||
// Constructor from pointer with optional dedup flag
|
||||
// ptr: pointer to TypedArrayImpl<T> to manage
|
||||
// ptr: pointer to TypedArray<T> to manage
|
||||
// dedup_flag: if true, marks as shared/mmap (won't delete); if false, takes
|
||||
// ownership (will delete)
|
||||
explicit TypedArray(TypedArrayImpl<T>* ptr, bool dedup_flag = false) noexcept
|
||||
explicit TypedArrayPtr(TypedArray<T>* ptr, bool dedup_flag = false) noexcept
|
||||
: _packed_data(0) {
|
||||
reset(ptr, dedup_flag);
|
||||
}
|
||||
|
||||
// Reconstruct TypedArray from packed_data.
|
||||
// Reconstruct TypedArrayPtr from packed_data.
|
||||
// No validity check of pointer address, so be careful to use this constructor.
|
||||
TypedArray(const uint64_t packed_data) : _packed_data(packed_data) {
|
||||
TypedArrayPtr(const uint64_t packed_data) : _packed_data(packed_data) {
|
||||
}
|
||||
|
||||
// Destructor - conditionally deletes based on dedup flag
|
||||
~TypedArray() {
|
||||
~TypedArrayPtr() {
|
||||
if (!is_dedup() && get() != nullptr) {
|
||||
delete get();
|
||||
}
|
||||
}
|
||||
|
||||
// Copy constructor - performs deep copy to avoid ownership issues
|
||||
TypedArray(const TypedArray& other) : _packed_data(0) {
|
||||
TypedArrayPtr(const TypedArrayPtr& other) : _packed_data(0) {
|
||||
if (other.is_null()) {
|
||||
// Source is null - nothing to copy
|
||||
_packed_data = 0;
|
||||
@@ -712,22 +576,22 @@ class TypedArray {
|
||||
_packed_data = other._packed_data;
|
||||
} else {
|
||||
// Source is owned - perform deep copy to avoid ownership conflicts
|
||||
TypedArrayImpl<T>* src_ptr = other.get();
|
||||
TypedArray<T>* src_ptr = other.get();
|
||||
if (src_ptr) {
|
||||
// Create new TypedArrayImpl with deep copy of data
|
||||
TypedArrayImpl<T>* new_ptr = new TypedArrayImpl<T>(*src_ptr);
|
||||
// Create new TypedArray with deep copy of data
|
||||
TypedArray<T>* new_ptr = new TypedArray<T>(*src_ptr);
|
||||
reset(new_ptr, false); // This copy owns the new data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move constructor - transfers ownership
|
||||
TypedArray(TypedArray&& other) noexcept : _packed_data(other._packed_data) {
|
||||
TypedArrayPtr(TypedArrayPtr&& other) noexcept : _packed_data(other._packed_data) {
|
||||
other._packed_data = 0; // Reset source to null
|
||||
}
|
||||
|
||||
// Copy assignment - performs deep copy to avoid ownership issues
|
||||
TypedArray& operator=(const TypedArray& other) {
|
||||
TypedArrayPtr& operator=(const TypedArrayPtr& other) {
|
||||
if (this != &other) {
|
||||
// Delete current resource if owned
|
||||
if (!is_dedup() && get() != nullptr) {
|
||||
@@ -743,9 +607,9 @@ class TypedArray {
|
||||
_packed_data = other._packed_data;
|
||||
} else {
|
||||
// Source is owned - perform deep copy
|
||||
TypedArrayImpl<T>* src_ptr = other.get();
|
||||
TypedArray<T>* src_ptr = other.get();
|
||||
if (src_ptr) {
|
||||
TypedArrayImpl<T>* new_ptr = new TypedArrayImpl<T>(*src_ptr);
|
||||
TypedArray<T>* new_ptr = new TypedArray<T>(*src_ptr);
|
||||
reset(new_ptr, false); // This copy owns the new data
|
||||
} else {
|
||||
_packed_data = 0;
|
||||
@@ -756,7 +620,7 @@ class TypedArray {
|
||||
}
|
||||
|
||||
// Move assignment - transfers ownership
|
||||
TypedArray& operator=(TypedArray&& other) noexcept {
|
||||
TypedArrayPtr& operator=(TypedArrayPtr&& other) noexcept {
|
||||
if (this != &other) {
|
||||
// Delete current resource if owned
|
||||
if (!is_dedup() && get() != nullptr) {
|
||||
@@ -791,7 +655,7 @@ class TypedArray {
|
||||
}
|
||||
|
||||
// Get the raw pointer
|
||||
TypedArrayImpl<T>* get() const noexcept {
|
||||
TypedArray<T>* get() const noexcept {
|
||||
uint64_t ptr_bits = _packed_data & PTR_MASK;
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
@@ -807,18 +671,18 @@ class TypedArray {
|
||||
// ARM64 user space addresses can have bit 47 set (0x0000... to 0x0000FFFF...)
|
||||
// and should not be sign-extended to kernel space addresses
|
||||
|
||||
return reinterpret_cast<TypedArrayImpl<T>*>(ptr_bits);
|
||||
return reinterpret_cast<TypedArray<T>*>(ptr_bits);
|
||||
}
|
||||
|
||||
// Pointer dereference operators
|
||||
TypedArrayImpl<T>* operator->() const noexcept { return get(); }
|
||||
TypedArray<T>* operator->() const noexcept { return get(); }
|
||||
|
||||
TypedArrayImpl<T>& operator*() const noexcept { return *get(); }
|
||||
TypedArray<T>& operator*() const noexcept { return *get(); }
|
||||
|
||||
// Convenience methods that forward to TypedArrayImpl
|
||||
using size_type = typename TypedArrayImpl<T>::size_type;
|
||||
using reference = typename TypedArrayImpl<T>::reference;
|
||||
using const_reference = typename TypedArrayImpl<T>::const_reference;
|
||||
// Convenience methods that forward to TypedArray
|
||||
using size_type = typename TypedArray<T>::size_type;
|
||||
using reference = typename TypedArray<T>::reference;
|
||||
using const_reference = typename TypedArray<T>::const_reference;
|
||||
|
||||
size_type size() const noexcept { return get() ? get()->size() : 0; }
|
||||
|
||||
@@ -828,15 +692,14 @@ class TypedArray {
|
||||
|
||||
reference at(size_type index) const { return get()->at(index); }
|
||||
|
||||
typename TypedArrayImpl<T>::pointer data() const noexcept {
|
||||
typename TypedArray<T>::pointer data() const noexcept {
|
||||
return get() ? get()->data() : nullptr;
|
||||
}
|
||||
|
||||
bool resize(size_type count) {
|
||||
void resize(size_type count) {
|
||||
if (get()) {
|
||||
return get()->resize(count);
|
||||
get()->resize(count);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
@@ -851,9 +714,9 @@ class TypedArray {
|
||||
|
||||
// Reset to new pointer with optional dedup flag
|
||||
// Deletes current pointer if owned
|
||||
void reset(TypedArrayImpl<T>* ptr = nullptr,
|
||||
void reset(TypedArray<T>* ptr = nullptr,
|
||||
bool dedup_flag = false) noexcept {
|
||||
TypedArrayImpl<T>* old_ptr = get();
|
||||
TypedArray<T>* old_ptr = get();
|
||||
bool old_is_dedup = is_dedup();
|
||||
|
||||
//DCOUT("TypedArray::reset: old_ptr=" << std::hex << old_ptr << " old_is_dedup=" << old_is_dedup
|
||||
@@ -894,8 +757,8 @@ class TypedArray {
|
||||
|
||||
// Release ownership without deleting
|
||||
// Returns the pointer and clears this instance
|
||||
TypedArrayImpl<T>* release() noexcept {
|
||||
TypedArrayImpl<T>* ptr = get();
|
||||
TypedArray<T>* release() noexcept {
|
||||
TypedArray<T>* ptr = get();
|
||||
_packed_data = 0;
|
||||
return ptr;
|
||||
}
|
||||
@@ -904,11 +767,11 @@ class TypedArray {
|
||||
uint64_t get_packed_value() const noexcept { return _packed_data; }
|
||||
|
||||
// Comparison operators
|
||||
bool operator==(const TypedArray& other) const noexcept {
|
||||
bool operator==(const TypedArrayPtr& other) const noexcept {
|
||||
return get() == other.get();
|
||||
}
|
||||
|
||||
bool operator!=(const TypedArray& other) const noexcept {
|
||||
bool operator!=(const TypedArrayPtr& other) const noexcept {
|
||||
return get() != other.get();
|
||||
}
|
||||
|
||||
@@ -929,16 +792,16 @@ class TypedArray {
|
||||
0x7FFF000000000000ULL; // Bits 48-62: reserved
|
||||
};
|
||||
|
||||
// Helper function to create owned TypedArray
|
||||
// Helper function to create owned TypedArrayPtr
|
||||
template <typename T>
|
||||
TypedArray<T> make_typed_array_ptr(TypedArrayImpl<T>* ptr) {
|
||||
return TypedArray<T>(ptr, false);
|
||||
TypedArrayPtr<T> make_typed_array_ptr(TypedArray<T>* ptr) {
|
||||
return TypedArrayPtr<T>(ptr, false);
|
||||
}
|
||||
|
||||
// Helper function to create dedup/mmap TypedArray
|
||||
// Helper function to create dedup/mmap TypedArrayPtr
|
||||
template <typename T>
|
||||
TypedArray<T> make_typed_array_ptr_dedup(TypedArrayImpl<T>* ptr) {
|
||||
return TypedArray<T>(ptr, true);
|
||||
TypedArrayPtr<T> make_typed_array_ptr_dedup(TypedArray<T>* ptr) {
|
||||
return TypedArrayPtr<T>(ptr, true);
|
||||
}
|
||||
|
||||
///
|
||||
@@ -1007,9 +870,9 @@ class TypedArrayView {
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor from TypedArrayImpl with type size validation
|
||||
// Constructor from TypedArray with type size validation
|
||||
template <typename U>
|
||||
TypedArrayView(const TypedArrayImpl<U>& typed_array) noexcept {
|
||||
TypedArrayView(const TypedArray<U>& typed_array) noexcept {
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"TypedArrayView: T must be trivially copyable");
|
||||
static_assert(std::is_trivially_copyable<U>::value,
|
||||
@@ -1026,9 +889,9 @@ class TypedArrayView {
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor from mutable TypedArrayImpl with type size validation
|
||||
// Constructor from mutable TypedArray with type size validation
|
||||
template <typename U>
|
||||
TypedArrayView(TypedArrayImpl<U>& typed_array) noexcept {
|
||||
TypedArrayView(TypedArray<U>& typed_array) noexcept {
|
||||
static_assert(std::is_trivially_copyable<T>::value,
|
||||
"TypedArrayView: T must be trivially copyable");
|
||||
static_assert(std::is_trivially_copyable<U>::value,
|
||||
@@ -1173,7 +1036,7 @@ class TypedArrayView {
|
||||
|
||||
// Non-member swap
|
||||
template <typename T>
|
||||
void swap(TypedArrayImpl<T>& lhs, TypedArrayImpl<T>& rhs) noexcept {
|
||||
void swap(TypedArray<T>& lhs, TypedArray<T>& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
@@ -1206,12 +1069,12 @@ TypedArrayView<const T> make_typed_array_view(const std::vector<T>& vec) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TypedArrayView<T> make_typed_array_view(TypedArrayImpl<T>& arr) {
|
||||
TypedArrayView<T> make_typed_array_view(TypedArray<T>& arr) {
|
||||
return TypedArrayView<T>(arr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TypedArrayView<const T> make_typed_array_view(const TypedArrayImpl<T>& arr) {
|
||||
TypedArrayView<const T> make_typed_array_view(const TypedArray<T>& arr) {
|
||||
return TypedArrayView<const T>(arr);
|
||||
}
|
||||
|
||||
@@ -1232,10 +1095,10 @@ TypedArrayView<T> reinterpret_typed_array_view_mutable(
|
||||
return view.template reinterpret_as_mutable<T>();
|
||||
}
|
||||
|
||||
// Convenience function to create TypedArrayImpl from span
|
||||
// Convenience function to create TypedArray from span
|
||||
template <typename T>
|
||||
TypedArrayImpl<T> make_typed_array(nonstd::span<const T> sp) {
|
||||
return TypedArrayImpl<T>(sp.data(), sp.size());
|
||||
TypedArray<T> make_typed_array(nonstd::span<const T> sp) {
|
||||
return TypedArray<T>(sp.data(), sp.size());
|
||||
}
|
||||
|
||||
///
|
||||
@@ -2140,12 +2003,12 @@ class ChunkedTypedArray {
|
||||
|
||||
// Get chunk at specific index (for advanced usage)
|
||||
// Returns nullptr if chunk_index is out of range or in mmap mode
|
||||
const TypedArrayImpl<uint8_t>* get_chunk(size_type chunk_index) const {
|
||||
const TypedArray<uint8_t>* get_chunk(size_type chunk_index) const {
|
||||
if (_is_mmap_mode || chunk_index >= _chunks.size()) return nullptr;
|
||||
return &_chunks[chunk_index];
|
||||
}
|
||||
|
||||
TypedArrayImpl<uint8_t>* get_chunk(size_type chunk_index) {
|
||||
TypedArray<uint8_t>* get_chunk(size_type chunk_index) {
|
||||
if (_is_mmap_mode || chunk_index >= _chunks.size()) return nullptr;
|
||||
return &_chunks[chunk_index];
|
||||
}
|
||||
@@ -2367,8 +2230,8 @@ class ChunkedTypedArray {
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<TypedArrayImpl<uint8_t>>
|
||||
_chunks; // Storage chunks using TypedArrayImpl (for copy mode)
|
||||
std::vector<TypedArray<uint8_t>>
|
||||
_chunks; // Storage chunks using TypedArray (for copy mode)
|
||||
std::vector<ChunkSpan>
|
||||
_mmap_spans; // Spans to external memory (for mmap mode)
|
||||
size_type _chunk_size_bytes =
|
||||
@@ -2425,12 +2288,12 @@ void swap(ChunkedTypedArray<T>& lhs, ChunkedTypedArray<T>& rhs) noexcept {
|
||||
/// Use this when TypedArray should manage the lifetime of the implementation.
|
||||
///
|
||||
/// Example:
|
||||
/// auto* impl = new TypedArrayImpl<float>(100);
|
||||
/// auto* impl = new TypedArray<float>(100);
|
||||
/// TypedArray<float> arr = MakeOwnedTypedArray(impl);
|
||||
/// // arr will delete impl when destroyed
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> MakeOwnedTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
inline TypedArray<T> MakeOwnedTypedArray(TypedArray<T>* ptr) {
|
||||
return TypedArray<T>(ptr, false); // dedup_flag = false: will delete
|
||||
}
|
||||
|
||||
@@ -2445,7 +2308,7 @@ inline TypedArray<T> MakeOwnedTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
/// // arr won't delete the cached array
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> MakeDedupTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
inline TypedArray<T> MakeDedupTypedArray(TypedArray<T>* ptr) {
|
||||
return TypedArray<T>(ptr, true); // dedup_flag = true: won't delete
|
||||
}
|
||||
|
||||
@@ -2454,7 +2317,7 @@ inline TypedArray<T> MakeDedupTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
/// Use this when the array is shared among multiple owners.
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> MakeSharedTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
inline TypedArray<T> MakeSharedTypedArray(TypedArray<T>* ptr) {
|
||||
return TypedArray<T>(ptr, true); // dedup_flag = true: won't delete
|
||||
}
|
||||
|
||||
@@ -2464,20 +2327,20 @@ inline TypedArray<T> MakeSharedTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
///
|
||||
/// Example:
|
||||
/// float* mmap_data = static_cast<float*>(mmap_ptr);
|
||||
/// auto* impl = new TypedArrayImpl<float>(mmap_data, count, true);
|
||||
/// auto* impl = new TypedArray<float>(mmap_data, count, true);
|
||||
/// TypedArray<float> arr = MakeMmapTypedArray(impl);
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> MakeMmapTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
inline TypedArray<T> MakeMmapTypedArray(TypedArray<T>* ptr) {
|
||||
return TypedArray<T>(ptr, true); // dedup_flag = true: won't delete
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TypedArrayImpl Factory Functions (Array Implementation)
|
||||
// TypedArray Factory Functions (Array Implementation)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
///
|
||||
/// Create TypedArrayImpl with owned copy of data
|
||||
/// Create TypedArray with owned copy of data
|
||||
/// Copies the data into internal storage.
|
||||
///
|
||||
/// Example:
|
||||
@@ -2485,39 +2348,12 @@ inline TypedArray<T> MakeMmapTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
/// auto arr = MakeTypedArrayCopy(data, 3);
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArrayImpl<T> MakeTypedArrayCopy(const T* data, size_t count) {
|
||||
return TypedArrayImpl<T>(data, count); // Copies data
|
||||
inline TypedArray<T> MakeTypedArrayCopy(const T* data, size_t count) {
|
||||
return TypedArray<T>(data, count); // Copies data
|
||||
}
|
||||
|
||||
///
|
||||
/// Create non-owning view over external memory
|
||||
/// Does not copy data, just references it. Caller must ensure memory lifetime.
|
||||
///
|
||||
/// Example:
|
||||
/// float external_buffer[1000];
|
||||
/// auto view = MakeTypedArrayView(external_buffer, 1000);
|
||||
/// // view doesn't own the data
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArrayImpl<T> MakeTypedArrayView(T* data, size_t count) {
|
||||
return TypedArrayImpl<T>(data, count, true); // is_view = true
|
||||
}
|
||||
|
||||
///
|
||||
/// Create non-owning view for memory-mapped data
|
||||
/// Alias for MakeTypedArrayView with clearer intent for mmap use cases.
|
||||
///
|
||||
/// Example:
|
||||
/// float* mmap_ptr = static_cast<float*>(mmap(fd, ...));
|
||||
/// auto arr = MakeTypedArrayMmap(mmap_ptr, element_count);
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArrayImpl<T> MakeTypedArrayMmap(T* data, size_t count) {
|
||||
return TypedArrayImpl<T>(data, count, true); // is_view = true
|
||||
}
|
||||
|
||||
///
|
||||
/// Create empty TypedArrayImpl with specified capacity
|
||||
/// Create empty TypedArray with specified capacity
|
||||
/// Reserves memory without initializing elements.
|
||||
///
|
||||
/// Example:
|
||||
@@ -2527,8 +2363,8 @@ inline TypedArrayImpl<T> MakeTypedArrayMmap(T* data, size_t count) {
|
||||
/// }
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArrayImpl<T> MakeTypedArrayReserved(size_t capacity) {
|
||||
TypedArrayImpl<T> arr;
|
||||
inline TypedArray<T> MakeTypedArrayReserved(size_t capacity) {
|
||||
TypedArray<T> arr;
|
||||
arr.reserve(capacity);
|
||||
return arr;
|
||||
}
|
||||
@@ -2547,7 +2383,7 @@ inline TypedArrayImpl<T> MakeTypedArrayReserved(size_t capacity) {
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> CreateOwnedTypedArray(const T* data, size_t count) {
|
||||
auto* impl = new TypedArrayImpl<T>(data, count);
|
||||
auto* impl = new TypedArray<T>(data, count);
|
||||
return MakeOwnedTypedArray(impl);
|
||||
}
|
||||
|
||||
@@ -2563,7 +2399,7 @@ inline TypedArray<T> CreateOwnedTypedArray(const T* data, size_t count) {
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> CreateOwnedTypedArray(size_t count) {
|
||||
auto* impl = new TypedArrayImpl<T>(count);
|
||||
auto* impl = new TypedArray<T>(count);
|
||||
return MakeOwnedTypedArray(impl);
|
||||
}
|
||||
|
||||
@@ -2576,7 +2412,7 @@ inline TypedArray<T> CreateOwnedTypedArray(size_t count) {
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> CreateOwnedTypedArray(size_t count, const T& value) {
|
||||
auto* impl = new TypedArrayImpl<T>(count, value);
|
||||
auto* impl = new TypedArray<T>(count, value);
|
||||
return MakeOwnedTypedArray(impl);
|
||||
}
|
||||
|
||||
@@ -2585,11 +2421,11 @@ inline TypedArray<T> CreateOwnedTypedArray(size_t count, const T& value) {
|
||||
/// Use this when storing in deduplication cache.
|
||||
///
|
||||
/// Example:
|
||||
/// TypedArrayImpl<int32_t>& cached = _dedup_int32_array[value_rep];
|
||||
/// TypedArray<int32_t>& cached = _dedup_int32_array[value_rep];
|
||||
/// TypedArray<int32_t> arr = CreateDedupTypedArray(&cached);
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> CreateDedupTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
inline TypedArray<T> CreateDedupTypedArray(TypedArray<T>* ptr) {
|
||||
return MakeDedupTypedArray(ptr);
|
||||
}
|
||||
|
||||
@@ -2603,7 +2439,7 @@ inline TypedArray<T> CreateDedupTypedArray(TypedArrayImpl<T>* ptr) {
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> CreateMmapTypedArray(T* data, size_t count) {
|
||||
auto* impl = new TypedArrayImpl<T>(data, count, true); // View mode
|
||||
auto* impl = new TypedArray<T>(data, count, true); // View mode
|
||||
return MakeMmapTypedArray(impl);
|
||||
}
|
||||
|
||||
@@ -2611,35 +2447,20 @@ inline TypedArray<T> CreateMmapTypedArray(T* data, size_t count) {
|
||||
/// Deep copy an existing TypedArray
|
||||
/// Creates a new independent copy with its own storage.
|
||||
///
|
||||
/// Example:
|
||||
/// TypedArray<double> original = ...;
|
||||
/// TypedArray<double> copy = DuplicateTypedArray(original);
|
||||
/// // copy is completely independent
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArray<T> DuplicateTypedArray(const TypedArray<T>& source) {
|
||||
if (!source || source.empty()) {
|
||||
return TypedArray<T>();
|
||||
}
|
||||
auto* impl = new TypedArrayImpl<T>(source.data(), source.size());
|
||||
return MakeOwnedTypedArray(impl);
|
||||
}
|
||||
|
||||
///
|
||||
/// Deep copy a TypedArrayImpl
|
||||
/// Deep copy a TypedArray
|
||||
/// Creates a new implementation with copied data.
|
||||
///
|
||||
/// Example:
|
||||
/// TypedArrayImpl<float> original = ...;
|
||||
/// TypedArrayImpl<float> copy = DuplicateTypedArrayImpl(original);
|
||||
/// TypedArray<float> original = ...;
|
||||
/// TypedArray<float> copy = DuplicateTypedArray(original);
|
||||
///
|
||||
template <typename T>
|
||||
inline TypedArrayImpl<T> DuplicateTypedArrayImpl(
|
||||
const TypedArrayImpl<T>& source) {
|
||||
inline TypedArray<T> DuplicateTypedArray(
|
||||
const TypedArray<T>& source) {
|
||||
if (source.empty()) {
|
||||
return TypedArrayImpl<T>();
|
||||
return TypedArray<T>();
|
||||
}
|
||||
return TypedArrayImpl<T>(source.data(), source.size());
|
||||
return TypedArray<T>(source.data(), source.size());
|
||||
}
|
||||
|
||||
} // namespace tinyusdz
|
||||
|
||||
@@ -402,186 +402,6 @@ void timesamples_test(void) {
|
||||
TEST_CHECK(!t3.has_value());
|
||||
}
|
||||
|
||||
// Test PODTimeSamples with underlying_type_id support
|
||||
// This tests that PODTimeSamples can get values using role types
|
||||
// even when the actual stored type is the underlying type.
|
||||
{
|
||||
PODTimeSamples samples;
|
||||
|
||||
// Test 1: Store as float3, retrieve as normal3f
|
||||
{
|
||||
value::float3 v1 = {1.0f, 2.0f, 3.0f};
|
||||
value::float3 v2 = {4.0f, 5.0f, 6.0f};
|
||||
|
||||
// Add samples as float3
|
||||
std::string err;
|
||||
TEST_CHECK(samples.add_sample(1.0, v1, &err));
|
||||
TEST_CHECK(samples.add_sample(2.0, v2, &err));
|
||||
|
||||
// Retrieve as normal3f (role type)
|
||||
value::normal3f retrieved;
|
||||
bool blocked;
|
||||
TEST_CHECK(samples.get_value_at(0, &retrieved, &blocked));
|
||||
TEST_CHECK(!blocked);
|
||||
|
||||
// Verify values
|
||||
TEST_CHECK(math::is_close(retrieved[0], 1.0f));
|
||||
TEST_CHECK(math::is_close(retrieved[1], 2.0f));
|
||||
TEST_CHECK(math::is_close(retrieved[2], 3.0f));
|
||||
|
||||
// Get second value
|
||||
TEST_CHECK(samples.get_value_at(1, &retrieved, &blocked));
|
||||
TEST_CHECK(math::is_close(retrieved[0], 4.0f));
|
||||
TEST_CHECK(math::is_close(retrieved[1], 5.0f));
|
||||
TEST_CHECK(math::is_close(retrieved[2], 6.0f));
|
||||
}
|
||||
|
||||
samples.clear();
|
||||
|
||||
// Test 2: Store as normal3f, retrieve as float3
|
||||
{
|
||||
value::normal3f n1 = {0.577f, 0.577f, 0.577f};
|
||||
value::normal3f n2 = {1.0f, 0.0f, 0.0f};
|
||||
|
||||
// Add samples as normal3f (will be stored as underlying float3)
|
||||
std::string err;
|
||||
TEST_CHECK(samples.add_sample(1.0, n1, &err));
|
||||
TEST_CHECK(samples.add_sample(2.0, n2, &err));
|
||||
|
||||
// Retrieve as float3 (underlying type)
|
||||
value::float3 retrieved;
|
||||
bool blocked;
|
||||
TEST_CHECK(samples.get_value_at(0, &retrieved, &blocked));
|
||||
TEST_CHECK(!blocked);
|
||||
|
||||
// Verify values
|
||||
TEST_CHECK(math::is_close(retrieved[0], 0.577f, 1e-3f));
|
||||
TEST_CHECK(math::is_close(retrieved[1], 0.577f, 1e-3f));
|
||||
TEST_CHECK(math::is_close(retrieved[2], 0.577f, 1e-3f));
|
||||
}
|
||||
|
||||
samples.clear();
|
||||
|
||||
// Test 3: Mixed role types with same underlying type
|
||||
{
|
||||
value::point3f p1 = {100.0f, 200.0f, 300.0f};
|
||||
|
||||
// Add sample as point3f
|
||||
std::string err;
|
||||
TEST_CHECK(samples.add_sample(1.0, p1, &err));
|
||||
|
||||
// Retrieve as color3f (different role, same underlying type)
|
||||
value::color3f color_retrieved;
|
||||
bool blocked;
|
||||
TEST_CHECK(samples.get_value_at(0, &color_retrieved, &blocked));
|
||||
|
||||
// Verify values
|
||||
TEST_CHECK(math::is_close(color_retrieved[0], 100.0f));
|
||||
TEST_CHECK(math::is_close(color_retrieved[1], 200.0f));
|
||||
TEST_CHECK(math::is_close(color_retrieved[2], 300.0f));
|
||||
|
||||
// Also retrieve as vector3f
|
||||
value::vector3f vec_retrieved;
|
||||
TEST_CHECK(samples.get_value_at(0, &vec_retrieved, &blocked));
|
||||
|
||||
TEST_CHECK(math::is_close(vec_retrieved[0], 100.0f));
|
||||
TEST_CHECK(math::is_close(vec_retrieved[1], 200.0f));
|
||||
TEST_CHECK(math::is_close(vec_retrieved[2], 300.0f));
|
||||
}
|
||||
|
||||
samples.clear();
|
||||
|
||||
// Test 4: get_value_at_time with role types
|
||||
{
|
||||
value::float3 v1 = {1.0f, 1.0f, 1.0f};
|
||||
value::float3 v2 = {2.0f, 2.0f, 2.0f};
|
||||
|
||||
// Add samples
|
||||
std::string err;
|
||||
TEST_CHECK(samples.add_sample(10.0, v1, &err));
|
||||
TEST_CHECK(samples.add_sample(20.0, v2, &err));
|
||||
|
||||
// Get value at specific time as normal3f
|
||||
value::normal3f retrieved;
|
||||
bool blocked;
|
||||
TEST_CHECK(samples.get_value_at_time(10.0, &retrieved, &blocked));
|
||||
TEST_CHECK(math::is_close(retrieved[0], 1.0f));
|
||||
|
||||
TEST_CHECK(samples.get_value_at_time(20.0, &retrieved, &blocked));
|
||||
TEST_CHECK(math::is_close(retrieved[0], 2.0f));
|
||||
}
|
||||
|
||||
samples.clear();
|
||||
|
||||
// Test 5: Blocked samples with role types
|
||||
{
|
||||
value::float3 v1 = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
// Add regular sample and blocked sample
|
||||
std::string err;
|
||||
TEST_CHECK(samples.add_sample(1.0, v1, &err));
|
||||
TEST_CHECK(samples.add_blocked_sample<value::float3>(2.0, &err));
|
||||
|
||||
// Get blocked sample as normal3f
|
||||
value::normal3f retrieved;
|
||||
bool blocked;
|
||||
TEST_CHECK(samples.get_value_at(1, &retrieved, &blocked));
|
||||
TEST_CHECK(blocked);
|
||||
|
||||
// Blocked values should be default-initialized
|
||||
TEST_CHECK(retrieved[0] == 0.0f);
|
||||
TEST_CHECK(retrieved[1] == 0.0f);
|
||||
TEST_CHECK(retrieved[2] == 0.0f);
|
||||
}
|
||||
|
||||
samples.clear();
|
||||
|
||||
// Test 6: Type consistency with multiple role types
|
||||
{
|
||||
value::float3 v1 = {1.0f, 1.0f, 1.0f};
|
||||
value::normal3f n1 = {2.0f, 2.0f, 2.0f};
|
||||
value::color3f c1 = {3.0f, 3.0f, 3.0f};
|
||||
|
||||
// Add samples with different role types but same underlying type
|
||||
std::string err;
|
||||
TEST_CHECK(samples.add_sample(1.0, v1, &err));
|
||||
TEST_CHECK(samples.add_sample(2.0, n1, &err));
|
||||
TEST_CHECK(samples.add_sample(3.0, c1, &err));
|
||||
|
||||
// Verify we can retrieve all as any of the role types
|
||||
value::point3f retrieved;
|
||||
TEST_CHECK(samples.get_value_at(0, &retrieved, nullptr));
|
||||
TEST_CHECK(math::is_close(retrieved[0], 1.0f));
|
||||
|
||||
TEST_CHECK(samples.get_value_at(1, &retrieved, nullptr));
|
||||
TEST_CHECK(math::is_close(retrieved[0], 2.0f));
|
||||
|
||||
TEST_CHECK(samples.get_value_at(2, &retrieved, nullptr));
|
||||
TEST_CHECK(math::is_close(retrieved[0], 3.0f));
|
||||
}
|
||||
|
||||
samples.clear();
|
||||
|
||||
// Test 7: Test with double3 and related role types
|
||||
{
|
||||
value::double3 d1 = {1.0, 2.0, 3.0};
|
||||
value::normal3d n1 = {4.0, 5.0, 6.0};
|
||||
|
||||
std::string err;
|
||||
TEST_CHECK(samples.add_sample(1.0, d1, &err));
|
||||
TEST_CHECK(samples.add_sample(2.0, n1, &err));
|
||||
|
||||
// Retrieve double3 as normal3d
|
||||
value::normal3d retrieved_n;
|
||||
TEST_CHECK(samples.get_value_at(0, &retrieved_n, nullptr));
|
||||
TEST_CHECK(math::is_close(retrieved_n[0], 1.0));
|
||||
|
||||
// Retrieve normal3d as double3
|
||||
value::double3 retrieved_d;
|
||||
TEST_CHECK(samples.get_value_at(1, &retrieved_d, nullptr));
|
||||
TEST_CHECK(math::is_close(retrieved_d[0], 4.0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test duplicate time entries (std::stable_sort preserves order)
|
||||
|
||||
Reference in New Issue
Block a user