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:
Syoyo Fujita
2026-01-09 04:10:32 +09:00
parent 9372bf99af
commit 9077664b3a
9 changed files with 528 additions and 4061 deletions

View File

@@ -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;

View File

@@ -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))) {

View File

@@ -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)

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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>;

View File

@@ -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

View File

@@ -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)