1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24
25 #include "pxr/pxr.h"
26 #include "crateFile.h"
27 #include "integerCoding.h"
28
29 #include "pxr/base/arch/demangle.h"
30 #include "pxr/base/arch/errno.h"
31 #include "pxr/base/arch/fileSystem.h"
32 #include "pxr/base/arch/regex.h"
33 #include "pxr/base/arch/systemInfo.h"
34 #include "pxr/base/gf/half.h"
35 #include "pxr/base/gf/matrix2d.h"
36 #include "pxr/base/gf/matrix3d.h"
37 #include "pxr/base/gf/matrix4d.h"
38 #include "pxr/base/gf/quatd.h"
39 #include "pxr/base/gf/quatf.h"
40 #include "pxr/base/gf/quath.h"
41 #include "pxr/base/gf/traits.h"
42 #include "pxr/base/gf/vec2d.h"
43 #include "pxr/base/gf/vec2f.h"
44 #include "pxr/base/gf/vec2h.h"
45 #include "pxr/base/gf/vec2i.h"
46 #include "pxr/base/gf/vec3d.h"
47 #include "pxr/base/gf/vec3f.h"
48 #include "pxr/base/gf/vec3h.h"
49 #include "pxr/base/gf/vec3i.h"
50 #include "pxr/base/gf/vec4d.h"
51 #include "pxr/base/gf/vec4f.h"
52 #include "pxr/base/gf/vec4h.h"
53 #include "pxr/base/gf/vec4i.h"
54 #include "pxr/base/tf/envSetting.h"
55 #include "pxr/base/tf/errorMark.h"
56 #include "pxr/base/tf/fastCompression.h"
57 #include "pxr/base/tf/getenv.h"
58 #include "pxr/base/tf/mallocTag.h"
59 #include "pxr/base/tf/ostreamMethods.h"
60 #include "pxr/base/tf/safeOutputFile.h"
61 #include "pxr/base/tf/stringUtils.h"
62 #include "pxr/base/tf/token.h"
63 #include "pxr/base/trace/trace.h"
64 #include "pxr/base/vt/dictionary.h"
65 #include "pxr/base/vt/value.h"
66 #include "pxr/base/work/dispatcher.h"
67 #include "pxr/base/work/singularTask.h"
68 #include "pxr/base/work/utils.h"
69 #include "pxr/base/work/withScopedParallelism.h"
70 #include "pxr/usd/ar/asset.h"
71 #include "pxr/usd/ar/resolvedPath.h"
72 #include "pxr/usd/ar/resolver.h"
73 #include "pxr/usd/sdf/assetPath.h"
74 #include "pxr/usd/sdf/layerOffset.h"
75 #include "pxr/usd/sdf/listOp.h"
76 #include "pxr/usd/sdf/path.h"
77 #include "pxr/usd/sdf/pathTable.h"
78 #include "pxr/usd/sdf/payload.h"
79 #include "pxr/usd/sdf/reference.h"
80 #include "pxr/usd/sdf/schema.h"
81 #include "pxr/usd/sdf/types.h"
82 #include "pxr/base/tf/registryManager.h"
83 #include "pxr/base/tf/type.h"
84
85 #include <tbb/concurrent_queue.h>
86
87 #include <iostream>
88 #include <memory>
89 #include <tuple>
90 #include <type_traits>
91
92 PXR_NAMESPACE_OPEN_SCOPE
93
94 static inline unsigned int
_GetPageShift(unsigned int mask)95 _GetPageShift(unsigned int mask)
96 {
97 unsigned int shift = 1;
98 mask = ~mask;
99 while (mask >>= 1) {
100 ++shift;
101 }
102 return shift;
103 }
104
105 static const unsigned int CRATE_PAGESIZE = ArchGetPageSize();
106 static const uint64_t CRATE_PAGEMASK =
107 ~(static_cast<uint64_t>(CRATE_PAGESIZE-1));
108 static const unsigned int CRATE_PAGESHIFT = _GetPageShift(CRATE_PAGEMASK);
109
TF_REGISTRY_FUNCTION(TfType)110 TF_REGISTRY_FUNCTION(TfType) {
111 TfType::Define<Usd_CrateFile::TimeSamples>();
112 }
113
114 #define DEFAULT_NEW_VERSION "0.8.0"
115 TF_DEFINE_ENV_SETTING(
116 USD_WRITE_NEW_USDC_FILES_AS_VERSION, DEFAULT_NEW_VERSION,
117 "When writing new Usd Crate files, write them as this version. "
118 "This must have the same major version as the software and have less or "
119 "equal minor and patch versions. This is only for new files; saving "
120 "edits to an existing file preserves its version.");
121
122 TF_DEFINE_ENV_SETTING(
123 USDC_MMAP_PREFETCH_KB, 0,
124 "If set to a nonzero value, attempt to disable the OS's prefetching "
125 "behavior for memory-mapped files and instead do simple aligned block "
126 "fetches of the given size instead. If necessary the setting value is "
127 "rounded up to the next whole multiple of the system's page size "
128 "(typically 4 KB).");
129
130 TF_DEFINE_ENV_SETTING(
131 USDC_ENABLE_ZERO_COPY_ARRAYS, true,
132 "Enable the zero-copy optimization for numeric array values whose in-file "
133 "representation matches the in-memory representation. With this "
134 "optimization, we create VtArrays that point directly into the memory "
135 "mapped region rather than copying the data to heap buffers.");
136
137 TF_DEFINE_ENV_SETTING(
138 USDC_USE_ASSET, false,
139 "If set, data for Crate files will be read using ArAsset::Read. Crate "
140 "will not use system I/O functions like mmap or pread directly for Crate "
141 "files on disk, but these functions may be used indirectly by ArAsset "
142 "implementations.");
143
_GetMMapPrefetchKB()144 static int _GetMMapPrefetchKB()
145 {
146 auto getKB = []() {
147 int setting = TfGetEnvSetting(USDC_MMAP_PREFETCH_KB);
148 int kb =
149 ((setting * 1024 + CRATE_PAGESIZE - 1) & CRATE_PAGEMASK) / 1024;
150 if (setting != kb) {
151 fprintf(stderr, "Rounded USDC_MMAP_PREFETCH_KB value %d to %d",
152 setting, kb);
153 }
154 return kb;
155 };
156 static int kb = getKB();
157 return kb;
158 }
159
160 #if AR_VERSION == 1
161 // Write nbytes bytes to fd at pos.
162 static inline int64_t
WriteToAsset(FILE * file,void const * bytes,int64_t nbytes,int64_t pos)163 WriteToAsset(FILE *file, void const *bytes, int64_t nbytes, int64_t pos) {
164 int64_t nwritten = ArchPWrite(file, bytes, nbytes, pos);
165 if (ARCH_UNLIKELY(nwritten < 0)) {
166 TF_RUNTIME_ERROR("Failed writing usdc data: %s",
167 ArchStrerror().c_str());
168 nwritten = 0;
169 }
170 return nwritten;
171 }
172 #else
173 // Write nbytes bytes to asset at pos.
174 static inline int64_t
WriteToAsset(ArWritableAsset * asset,void const * bytes,int64_t nbytes,int64_t pos)175 WriteToAsset(ArWritableAsset* asset,
176 void const *bytes, int64_t nbytes, int64_t pos)
177 {
178 TfErrorMark m;
179
180 int64_t nwritten = asset->Write(bytes, nbytes, pos);
181 if (ARCH_UNLIKELY(nwritten != nbytes)) {
182 // Aggregate error messages into a single runtime error for brevity
183 std::string errMsg;
184 if (!m.IsClean()) {
185 std::vector<std::string> errs;
186 for (const TfError& e : m) {
187 errs.push_back(e.GetCommentary());
188 }
189 errMsg = ": ";
190 errMsg += TfStringJoin(errs, "; ");
191 }
192
193 TF_RUNTIME_ERROR("Failed writing usdc data%s", errMsg.c_str());
194 nwritten = 0;
195 }
196 return nwritten;
197 }
198 #endif
199
200 namespace Usd_CrateFile
201 {
202 // Metafunction that determines if a T instance can be read/written by simple
203 // bitwise copy.
204 template <class T>
205 struct _IsBitwiseReadWrite {
206 static const bool value =
207 std::is_enum<T>::value ||
208 std::is_arithmetic<T>::value ||
209 std::is_same<T, GfHalf>::value ||
210 std::is_trivial<T>::value ||
211 GfIsGfVec<T>::value ||
212 GfIsGfMatrix<T>::value ||
213 GfIsGfQuat<T>::value ||
214 std::is_base_of<Index, T>::value;
215 };
216 } // Usd_CrateFile
217
218 namespace {
219
220 // We use type char and a deleter for char[] instead of just using
221 // type char[] due to a (now fixed) bug in libc++ in LLVM. See
222 // https://llvm.org/bugs/show_bug.cgi?id=18350.
223 typedef std::unique_ptr<char, std::default_delete<char[]> > RawDataPtr;
224
225 using namespace Usd_CrateFile;
226
227 // To add a new section, add a name here and add that name to _KnownSections
228 // below, then add handling for it in _Write and _ReadStructuralSections.
229 constexpr _SectionName _TokensSectionName = "TOKENS";
230 constexpr _SectionName _StringsSectionName = "STRINGS";
231 constexpr _SectionName _FieldsSectionName = "FIELDS";
232 constexpr _SectionName _FieldSetsSectionName = "FIELDSETS";
233 constexpr _SectionName _PathsSectionName = "PATHS";
234 constexpr _SectionName _SpecsSectionName = "SPECS";
235
236 constexpr _SectionName _KnownSections[] = {
237 _TokensSectionName, _StringsSectionName, _FieldsSectionName,
238 _FieldSetsSectionName, _PathsSectionName, _SpecsSectionName
239 };
240
_IsInlinedImpl(string *)241 constexpr bool _IsInlinedImpl(string *) { return true; }
_IsInlinedImpl(TfToken *)242 constexpr bool _IsInlinedImpl(TfToken *) { return true; }
_IsInlinedImpl(SdfPath *)243 constexpr bool _IsInlinedImpl(SdfPath *) { return true; }
_IsInlinedImpl(SdfAssetPath *)244 constexpr bool _IsInlinedImpl(SdfAssetPath *) { return true; }
245 template <class T>
_IsInlinedImpl(T *)246 constexpr bool _IsInlinedImpl(T *) {
247 return sizeof(T) <= sizeof(uint32_t) && _IsBitwiseReadWrite<T>::value;
248 }
249 template <class T>
_IsInlinedType()250 constexpr bool _IsInlinedType() {
251 return _IsInlinedImpl(static_cast<T *>(nullptr));
252 }
253
254 #define xx(ENUMNAME, _unused1, CPPTYPE, _unused2) \
255 constexpr TypeEnum _TypeEnumForImpl(CPPTYPE *) { \
256 return TypeEnum::ENUMNAME; \
257 }
258 #include "crateDataTypes.h"
259 #undef xx
260 template <class T>
TypeEnumFor()261 constexpr Usd_CrateFile::TypeEnum TypeEnumFor() {
262 return _TypeEnumForImpl(static_cast<T *>(nullptr));
263 }
264
265 template <class T> struct ValueTypeTraits {};
266 // Generate value type traits providing enum value, array support, and whether
267 // or not the value may be inlined.
268 #define xx(ENUMNAME, _unused, CPPTYPE, SUPPORTSARRAY) \
269 template <> struct ValueTypeTraits<CPPTYPE> { \
270 static constexpr bool supportsArray = SUPPORTSARRAY; \
271 static constexpr bool isInlined = _IsInlinedType<CPPTYPE>(); \
272 };
273 #include "crateDataTypes.h"
274 #undef xx
275
276 template <class T>
ValueRepFor(uint64_t payload=0)277 static constexpr ValueRep ValueRepFor(uint64_t payload = 0) {
278 return ValueRep(TypeEnumFor<T>(),
279 ValueTypeTraits<T>::isInlined, /*isArray=*/false, payload);
280 }
281
282 template <class T>
ValueRepForArray(uint64_t payload=0)283 static constexpr ValueRep ValueRepForArray(uint64_t payload = 0) {
284 return ValueRep(TypeEnumFor<T>(),
285 /*isInlined=*/false, /*isArray=*/true, payload);
286 }
287
288 template <class T>
RoundToPageAddr(T * addr)289 T *RoundToPageAddr(T *addr) {
290 return reinterpret_cast<T *>(
291 reinterpret_cast<uintptr_t>(addr) & CRATE_PAGEMASK);
292 }
293
294 template <class T>
GetPageNumber(T * addr)295 uint64_t GetPageNumber(T *addr) {
296 return reinterpret_cast<uintptr_t>(addr) >> CRATE_PAGESHIFT;
297 }
298
299 } // anon
300
301
302 namespace Usd_CrateFile {
303
304 // XXX: These checks ensure VtValue can hold ValueRep in the lightest
305 // possible way -- WBN not to rely on internal knowledge of that.
306 static_assert(boost::has_trivial_constructor<ValueRep>::value, "");
307 static_assert(boost::has_trivial_copy<ValueRep>::value, "");
308 static_assert(boost::has_trivial_assign<ValueRep>::value, "");
309 static_assert(boost::has_trivial_destructor<ValueRep>::value, "");
310
311 using namespace Usd_CrateValueInliners;
312
313 using std::make_pair;
314 using std::string;
315 using std::tuple;
316 using std::unique_ptr;
317 using std::unordered_map;
318 using std::vector;
319
320 // Version history:
321 // 0.9.0: Added support for the timecode and timecode[] value types.
322 // 0.8.0: Added support for SdfPayloadListOp values and SdfPayload values with
323 // layer offsets.
324 // 0.7.0: Array sizes written as 64 bit ints.
325 // 0.6.0: Compressed (scalar) floating point arrays that are either all ints or
326 // can be represented efficiently with a lookup table.
327 // 0.5.0: Compressed (u)int & (u)int64 arrays, arrays no longer store '1' rank.
328 // 0.4.0: Compressed structural sections.
329 // 0.3.0: (broken, unused)
330 // 0.2.0: Added support for prepend and append fields of SdfListOp.
331 // 0.1.0: Fixed structure layout issue encountered in Windows port.
332 // See _PathItemHeader_0_0_1.
333 // 0.0.1: Initial release.
334 constexpr uint8_t USDC_MAJOR = 0;
335 constexpr uint8_t USDC_MINOR = 9;
336 constexpr uint8_t USDC_PATCH = 0;
337
338 struct CrateFile::Version
339 {
340 // Not named 'major' since that's a macro name conflict on POSIXes.
341 uint8_t majver, minver, patchver;
342
VersionUsd_CrateFile::CrateFile::Version343 constexpr Version() : Version(0,0,0) {}
VersionUsd_CrateFile::CrateFile::Version344 constexpr Version(uint8_t majver, uint8_t minver, uint8_t patchver)
345 : majver(majver), minver(minver), patchver(patchver) {}
346
VersionUsd_CrateFile::CrateFile::Version347 explicit Version(CrateFile::_BootStrap const &boot)
348 : Version(boot.version[0], boot.version[1], boot.version[2]) {}
349
FromStringUsd_CrateFile::CrateFile::Version350 static Version FromString(char const *str) {
351 uint32_t maj, min, pat;
352 if (sscanf(str, "%u.%u.%u", &maj, &min, &pat) != 3 ||
353 maj > 255 || min > 255 || pat > 255) {
354 return Version();
355 }
356 return Version(maj, min, pat);
357 }
358
AsIntUsd_CrateFile::CrateFile::Version359 constexpr uint32_t AsInt() const {
360 return static_cast<uint32_t>(majver) << 16 |
361 static_cast<uint32_t>(minver) << 8 |
362 static_cast<uint32_t>(patchver);
363 }
364
AsStringUsd_CrateFile::CrateFile::Version365 std::string AsString() const {
366 return TfStringPrintf("%" PRId8 ".%" PRId8 ".%" PRId8,
367 majver, minver, patchver);
368 }
369
IsValidUsd_CrateFile::CrateFile::Version370 bool IsValid() const { return AsInt() != 0; }
371
372 // Return true if fileVer has the same major version as this, and has a
373 // lesser or same minor version. Patch version irrelevant, since the
374 // versioning scheme specifies that patch level changes are
375 // forward-compatible.
CanReadUsd_CrateFile::CrateFile::Version376 bool CanRead(Version const &fileVer) const {
377 return fileVer.majver == majver && fileVer.minver <= minver;
378 }
379
380 // Return true if fileVer has the same major version as this, and has a
381 // lesser minor version, or has the same minor version and a lesser or equal
382 // patch version.
CanWriteUsd_CrateFile::CrateFile::Version383 bool CanWrite(Version const &fileVer) const {
384 return fileVer.majver == majver &&
385 (fileVer.minver < minver ||
386 (fileVer.minver == minver && fileVer.patchver <= patchver));
387 }
388
389 #define LOGIC_OP(op) \
390 constexpr bool operator op(Version const &other) const { \
391 return AsInt() op other.AsInt(); \
392 }
393 LOGIC_OP(==); LOGIC_OP(!=);
394 LOGIC_OP(<); LOGIC_OP(>);
395 LOGIC_OP(<=); LOGIC_OP(>=);
396 #undef LOGIC_OP
397 };
398
399 constexpr CrateFile::Version
400 _SoftwareVersion { USDC_MAJOR, USDC_MINOR, USDC_PATCH };
401
402 static CrateFile::Version
_GetVersionForNewlyCreatedFiles()403 _GetVersionForNewlyCreatedFiles() {
404 // Read the env setting and try to parse a version. If that fails to
405 // give a version this software is capable of writing, fall back to the
406 // _SoftwareVersion.
407 string setting = TfGetEnvSetting(USD_WRITE_NEW_USDC_FILES_AS_VERSION);
408 auto ver = CrateFile::Version::FromString(setting.c_str());
409 if (!ver.IsValid() || !_SoftwareVersion.CanWrite(ver)) {
410 TF_WARN("Invalid value '%s' for USD_WRITE_NEW_USDC_FILES_AS_VERSION - "
411 "falling back to default '%s'",
412 setting.c_str(), DEFAULT_NEW_VERSION);
413 ver = CrateFile::Version::FromString(DEFAULT_NEW_VERSION);
414 }
415 return ver;
416 }
417
418 static CrateFile::Version
GetVersionForNewlyCreatedFiles()419 GetVersionForNewlyCreatedFiles() {
420 static CrateFile::Version ver = _GetVersionForNewlyCreatedFiles();
421 return ver;
422 }
423
424 constexpr char const *USDC_IDENT = "PXR-USDC"; // 8 chars.
425
426 struct _PathItemHeader_0_0_1 {
_PathItemHeader_0_0_1Usd_CrateFile::_PathItemHeader_0_0_1427 _PathItemHeader_0_0_1() {}
_PathItemHeader_0_0_1Usd_CrateFile::_PathItemHeader_0_0_1428 _PathItemHeader_0_0_1(PathIndex pi, TokenIndex ti, uint8_t bs)
429 : index(pi), elementTokenIndex(ti), bits(bs) {}
430
431 // Deriving _BitwiseReadWrite and having members PathIndex and TokenIndex
432 // that derive _BitwiseReadWrite caused gcc on linux and mac to leave 4
433 // bytes at the head of this structure, making the whole thing 16 bytes,
434 // with the members starting at offset 4. This was revealed in the Windows
435 // port since MSVC made this struct 12 bytes, as intended. To fix this we
436 // have two versions of the struct. Version 0.0.1 files read/write this
437 // structure. Version 0.1.0 and newer read/write the new one.
438 uint32_t _unused_padding_;
439
440 PathIndex index;
441 TokenIndex elementTokenIndex;
442 uint8_t bits;
443 };
444 template <>
445 struct _IsBitwiseReadWrite<_PathItemHeader_0_0_1> : std::true_type {};
446
447 struct _PathItemHeader {
_PathItemHeaderUsd_CrateFile::_PathItemHeader448 _PathItemHeader() {}
_PathItemHeaderUsd_CrateFile::_PathItemHeader449 _PathItemHeader(PathIndex pi, TokenIndex ti, uint8_t bs)
450 : index(pi), elementTokenIndex(ti), bits(bs) {}
451 static const uint8_t HasChildBit = 1 << 0;
452 static const uint8_t HasSiblingBit = 1 << 1;
453 static const uint8_t IsPrimPropertyPathBit = 1 << 2;
454 PathIndex index;
455 TokenIndex elementTokenIndex;
456 uint8_t bits;
457 };
458 template <>
459 struct _IsBitwiseReadWrite<_PathItemHeader> : std::true_type {};
460
461 struct _ListOpHeader {
462 enum _Bits { IsExplicitBit = 1 << 0,
463 HasExplicitItemsBit = 1 << 1,
464 HasAddedItemsBit = 1 << 2,
465 HasDeletedItemsBit = 1 << 3,
466 HasOrderedItemsBit = 1 << 4,
467 HasPrependedItemsBit = 1 << 5,
468 HasAppendedItemsBit = 1 << 6 };
469
_ListOpHeaderUsd_CrateFile::_ListOpHeader470 _ListOpHeader() : bits(0) {}
471
472 template <class T>
_ListOpHeaderUsd_CrateFile::_ListOpHeader473 explicit _ListOpHeader(SdfListOp<T> const &op) : bits(0) {
474 bits |= op.IsExplicit() ? IsExplicitBit : 0;
475 bits |= op.GetExplicitItems().size() ? HasExplicitItemsBit : 0;
476 bits |= op.GetAddedItems().size() ? HasAddedItemsBit : 0;
477 bits |= op.GetPrependedItems().size() ? HasPrependedItemsBit : 0;
478 bits |= op.GetAppendedItems().size() ? HasAppendedItemsBit : 0;
479 bits |= op.GetDeletedItems().size() ? HasDeletedItemsBit : 0;
480 bits |= op.GetOrderedItems().size() ? HasOrderedItemsBit : 0;
481 }
482
IsExplicitUsd_CrateFile::_ListOpHeader483 bool IsExplicit() const { return bits & IsExplicitBit; }
484
HasExplicitItemsUsd_CrateFile::_ListOpHeader485 bool HasExplicitItems() const { return bits & HasExplicitItemsBit; }
HasAddedItemsUsd_CrateFile::_ListOpHeader486 bool HasAddedItems() const { return bits & HasAddedItemsBit; }
HasPrependedItemsUsd_CrateFile::_ListOpHeader487 bool HasPrependedItems() const { return bits & HasPrependedItemsBit; }
HasAppendedItemsUsd_CrateFile::_ListOpHeader488 bool HasAppendedItems() const { return bits & HasAppendedItemsBit; }
HasDeletedItemsUsd_CrateFile::_ListOpHeader489 bool HasDeletedItems() const { return bits & HasDeletedItemsBit; }
HasOrderedItemsUsd_CrateFile::_ListOpHeader490 bool HasOrderedItems() const { return bits & HasOrderedItemsBit; }
491
492 uint8_t bits;
493 };
494 template <> struct _IsBitwiseReadWrite<_ListOpHeader> : std::true_type {};
495
~_FileRange()496 CrateFile::_FileRange::~_FileRange()
497 {
498 if (file && hasOwnership) {
499 fclose(file);
500 }
501 }
502
~_FileMapping()503 CrateFile::_FileMapping::~_FileMapping()
504 {
505 _DetachReferencedRanges();
506 }
507
508 CrateFile::_FileMapping::ZeroCopySource *
AddRangeReference(void * addr,size_t numBytes)509 CrateFile::_FileMapping::AddRangeReference(void *addr, size_t numBytes)
510 {
511 auto iresult = _outstandingRanges.emplace(this, addr, numBytes);
512 // If we take the source's count from 0 -> 1, add a reference to the
513 // mapping.
514 if (iresult.first->NewRef()) {
515 intrusive_ptr_add_ref(this);
516 }
517 return &(*iresult.first);
518 }
519
520 // The 'start' arg must be volatile so we actually emit the "noop" store
521 // operations that "write" to the pages.
522 static void
TouchPages(char volatile * start,size_t numPages)523 TouchPages(char volatile *start, size_t numPages)
524 {
525 while (numPages--) {
526 *start = *start; // Don't change content, but cause a write. This
527 // forces the VM to detach the page from its mapped
528 // file backing and make it swap-backed instead
529 // (copy-on-write). This is sometimes called a "silent
530 // store". No current hw architecture "optimizes out"
531 // silent stores.
532 start += CRATE_PAGESIZE;
533 }
534 }
535
536 void
_DetachReferencedRanges()537 CrateFile::_FileMapping::_DetachReferencedRanges()
538 {
539 // At this moment, we're guaranteed that no ZeroCopySource objects'
540 // reference counts will increase (and in particular go from 0 to 1) since
541 // the mapping is being destroyed. Similarly no new _outstandingRanges
542 // can be created.
543 for (auto const &zeroCopy: _outstandingRanges) {
544 // This is racy, but benign. If we see a nonzero count that's
545 // concurrently being zeroed, we just do possibly unneeded work. The
546 // crucial thing is that we'll never see a zero count that could
547 // possibly become nonzero again.
548 if (zeroCopy.IsInUse()) {
549 // Calculate the page-aligned start address and the number of pages
550 // we need to touch.
551 auto addrAsInt = reinterpret_cast<uintptr_t>(zeroCopy.GetAddr());
552 int64_t pageStart = addrAsInt / CRATE_PAGESIZE;
553 int64_t pageEnd =
554 ((addrAsInt + zeroCopy.GetNumBytes() - 1) / CRATE_PAGESIZE) + 1;
555 TouchPages(reinterpret_cast<char *>(pageStart * CRATE_PAGESIZE),
556 pageEnd - pageStart);
557 }
558 }
559 }
560
ZeroCopySource(CrateFile::_FileMapping * m,void * addr,size_t numBytes)561 CrateFile::_FileMapping::ZeroCopySource::ZeroCopySource(
562 CrateFile::_FileMapping *m,
563 void *addr, size_t numBytes)
564 : Vt_ArrayForeignDataSource(_Detached)
565 , _mapping(m)
566 , _addr(addr)
567 , _numBytes(numBytes) {}
568
operator ==(ZeroCopySource const & other) const569 bool CrateFile::_FileMapping::ZeroCopySource::operator==(
570 ZeroCopySource const &other) const {
571 return _mapping == other._mapping &&
572 _addr == other._addr && _numBytes == other._numBytes;
573 }
574
_Detached(Vt_ArrayForeignDataSource * selfBase)575 void CrateFile::_FileMapping::ZeroCopySource::_Detached(
576 Vt_ArrayForeignDataSource *selfBase) {
577 auto *self = static_cast<ZeroCopySource *>(selfBase);
578 intrusive_ptr_release(self->_mapping);
579 }
580
581 template <class FileMappingPtr>
582 struct _MmapStream {
583 // Mmap streams support zero-copy arrays; direct references into file-mapped
584 // memory.
585 static constexpr bool SupportsZeroCopy = true;
586
_MmapStreamUsd_CrateFile::_MmapStream587 explicit _MmapStream(FileMappingPtr const &mapping, char *debugPageMap)
588 : _cur(mapping->GetMapStart())
589 , _mapping(mapping)
590 , _debugPageMap(debugPageMap)
591 , _prefetchKB(_GetMMapPrefetchKB()) {}
592
DisablePrefetchUsd_CrateFile::_MmapStream593 _MmapStream &DisablePrefetch() {
594 _prefetchKB = 0;
595 return *this;
596 }
597
ReadUsd_CrateFile::_MmapStream598 inline void Read(void *dest, size_t nBytes) {
599 #ifdef PXR_PREFER_SAFETY_OVER_SPEED
600 const bool doRangeChecks = true;
601 #else
602 const bool doRangeChecks = false;
603 #endif
604
605 // Range check first.
606 if (doRangeChecks) {
607 char const *mapStart = _mapping->GetMapStart();
608 size_t mapLen = _mapping->GetLength();
609
610 bool inRange = mapStart <= _cur &&
611 (_cur + nBytes) <= (mapStart + mapLen);
612
613 if (ARCH_UNLIKELY(!inRange)) {
614 ptrdiff_t offset = _cur - mapStart;
615 TF_RUNTIME_ERROR(
616 "Read out-of-bounds: %zd bytes at offset %td in "
617 "a mapping of length %zd",
618 nBytes, offset, mapLen);
619 memset(dest, 0x99, nBytes);
620 return;
621 }
622 }
623
624 if (ARCH_UNLIKELY(_debugPageMap)) {
625 auto mapStart = _mapping->GetMapStart();
626 int64_t pageZero = GetPageNumber(mapStart);
627 int64_t firstPage = GetPageNumber(_cur) - pageZero;
628 int64_t lastPage = GetPageNumber(_cur + nBytes - 1) - pageZero;
629 memset(_debugPageMap + firstPage, 1, lastPage - firstPage + 1);
630 }
631
632 if (_prefetchKB) {
633 // Custom aligned chunk "prefetch".
634 auto mapStart = _mapping->GetMapStart();
635 auto mapStartPage = RoundToPageAddr(mapStart);
636 const auto chunkBytes = _prefetchKB * 1024;
637 auto firstChunk = (_cur-mapStartPage) / chunkBytes;
638 auto lastChunk = ((_cur-mapStartPage) + nBytes) / chunkBytes;
639
640 char const *beginAddr = mapStartPage + firstChunk * chunkBytes;
641 char const *endAddr = mapStartPage + std::min(
642 _mapping->GetLength() + (mapStart-mapStartPage),
643 (lastChunk + 1) * chunkBytes);
644
645 ArchMemAdvise(reinterpret_cast<void *>(
646 const_cast<char *>(beginAddr)),
647 endAddr-beginAddr, ArchMemAdviceWillNeed);
648 }
649
650 memcpy(dest, _cur, nBytes);
651
652 _cur += nBytes;
653 }
TellUsd_CrateFile::_MmapStream654 inline int64_t Tell() const {
655 return _cur - _mapping->GetMapStart();
656 }
SeekUsd_CrateFile::_MmapStream657 inline void Seek(int64_t offset) {
658 _cur = _mapping->GetMapStart() + offset;
659 }
PrefetchUsd_CrateFile::_MmapStream660 inline void Prefetch(int64_t offset, int64_t size) {
661 ArchMemAdvise(
662 _mapping->GetMapStart() + offset, size, ArchMemAdviceWillNeed);
663 }
664
665 Vt_ArrayForeignDataSource *
CreateZeroCopyDataSourceUsd_CrateFile::_MmapStream666 CreateZeroCopyDataSource(void *addr, size_t numBytes) {
667 char const *mapStart = _mapping->GetMapStart();
668 char const *chAddr = static_cast<char *>(addr);
669 size_t mapLen = _mapping->GetLength();
670 bool inRange = mapStart <= chAddr &&
671 (chAddr + numBytes) <= (mapStart + mapLen);
672
673 if (ARCH_UNLIKELY(!inRange)) {
674 ptrdiff_t offset = chAddr - mapStart;
675 TF_RUNTIME_ERROR(
676 "Zero-copy data range out-of-bounds: %zd bytes at offset "
677 "%td in a mapping of length %zd",
678 numBytes, offset, mapLen);
679 return nullptr;
680 }
681 return _mapping->AddRangeReference(addr, numBytes);
682 }
683
TellMemoryAddressUsd_CrateFile::_MmapStream684 inline void *TellMemoryAddress() const {
685 return _cur;
686 }
687
688 private:
689 char *_cur;
690 FileMappingPtr _mapping;
691 char *_debugPageMap;
692 int _prefetchKB;
693 };
694
695 template <class FileMappingPtr>
696 inline _MmapStream<FileMappingPtr>
_MakeMmapStream(FileMappingPtr const & mapping,char * debugPageMap)697 _MakeMmapStream(FileMappingPtr const &mapping, char *debugPageMap) {
698 return _MmapStream<FileMappingPtr>(mapping, debugPageMap);
699 }
700
701 struct _PreadStream {
702 // Pread streams do not support zero-copy arrays.
703 static constexpr bool SupportsZeroCopy = false;
704
705 template <class FileRange>
_PreadStreamUsd_CrateFile::_PreadStream706 explicit _PreadStream(FileRange const &fr)
707 : _start(fr.startOffset)
708 , _cur(0)
709 , _file(fr.file) {}
ReadUsd_CrateFile::_PreadStream710 inline void Read(void *dest, size_t nBytes) {
711 _cur += ArchPRead(_file, dest, nBytes, _start + _cur);
712 }
TellUsd_CrateFile::_PreadStream713 inline int64_t Tell() const { return _cur; }
SeekUsd_CrateFile::_PreadStream714 inline void Seek(int64_t offset) { _cur = offset; }
PrefetchUsd_CrateFile::_PreadStream715 inline void Prefetch(int64_t offset, int64_t size) {
716 ArchFileAdvise(_file, _start+offset, size, ArchFileAdviceWillNeed);
717 }
718
719 private:
720 int64_t _start;
721 int64_t _cur;
722 FILE *_file;
723 };
724
725 struct _AssetStream {
726 // Asset streams do not support zero-copy arrays.
727 static constexpr bool SupportsZeroCopy = false;
728
_AssetStreamUsd_CrateFile::_AssetStream729 explicit _AssetStream(ArAssetSharedPtr const &asset)
730 : _asset(asset)
731 , _cur(0) {}
ReadUsd_CrateFile::_AssetStream732 inline void Read(void *dest, size_t nBytes) {
733 _cur += _asset->Read(dest, nBytes, _cur);
734 }
TellUsd_CrateFile::_AssetStream735 inline int64_t Tell() const { return _cur; }
SeekUsd_CrateFile::_AssetStream736 inline void Seek(int64_t offset) { _cur = offset; }
PrefetchUsd_CrateFile::_AssetStream737 inline void Prefetch(int64_t offset, int64_t size) {
738 /* no prefetch impl */
739 }
740
741 private:
742 ArAssetSharedPtr _asset;
743 int64_t _cur;
744 };
745
746 ////////////////////////////////////////////////////////////////////////
747 // _TableOfContents
748 CrateFile::_Section const *
GetSection(_SectionName name) const749 CrateFile::_TableOfContents::GetSection(_SectionName name) const
750 {
751 for (auto const &sec: sections) {
752 if (name == sec.name)
753 return &sec;
754 }
755 TF_RUNTIME_ERROR("Crate file missing %s section", name.c_str());
756 return nullptr;
757 }
758
759 int64_t
GetMinimumSectionStart() const760 CrateFile::_TableOfContents::GetMinimumSectionStart() const
761 {
762 auto theMin = std::min_element(
763 sections.begin(), sections.end(),
764 [](_Section const &l, _Section const &r) { return l.start < r.start; });
765
766 return theMin == sections.end() ? sizeof(_BootStrap) : theMin->start;
767 }
768
769 ////////////////////////////////////////////////////////////////////////
770 // _BufferedOutput
771 class CrateFile::_BufferedOutput
772 {
773 public:
774 #if AR_VERSION == 1
775 using OutputType = FILE*;
776 #else
777 using OutputType = ArWritableAsset*;
778 #endif
779
780 // Current buffer size is 512k.
781 static const size_t BufferCap = 512*1024;
782
783 // Helper move-only buffer object -- memory + valid size.
784 struct _Buffer {
785 _Buffer() = default;
786 _Buffer(_Buffer const &) = delete;
787 _Buffer &operator=(_Buffer const &) = delete;
788 _Buffer(_Buffer &&) = default;
789 _Buffer &operator=(_Buffer &&) = default;
790
791 RawDataPtr bytes { new char[BufferCap] };
792 int64_t size = 0;
793 };
794
_BufferedOutput(OutputType file)795 explicit _BufferedOutput(OutputType file)
796 : _filePos(0)
797 , _file(file)
798 , _bufferPos(0)
799 , _writeTask(_dispatcher, [this]() { _DoWrites(); }) {
800 // Create NumBuffers buffers. One is _buffer, the remainder live in
801 // _freeBuffers.
802 constexpr const int NumBuffers = 8;
803 for (int i = 1; i != NumBuffers; ++i) {
804 _freeBuffers.push(_Buffer());
805 }
806 }
807
Flush()808 inline void Flush() {
809 _FlushBuffer();
810 _dispatcher.Wait();
811 }
812
Write(void const * bytes,int64_t nBytes)813 inline void Write(void const *bytes, int64_t nBytes) {
814 // Write and flush as needed.
815 while (nBytes) {
816 int64_t available = BufferCap - (_filePos - _bufferPos);
817 int64_t numToWrite = std::min(available, nBytes);
818
819 _WriteToBuffer(bytes, numToWrite);
820
821 bytes = static_cast<char const *>(bytes) + numToWrite;
822 nBytes -= numToWrite;
823
824 if (numToWrite == available)
825 _FlushBuffer();
826 }
827 }
828
Tell() const829 inline int64_t Tell() const { return _filePos; }
830
Seek(int64_t offset)831 inline void Seek(int64_t offset) {
832 // If the seek lands in a valid buffer region, then just adjust the
833 // _filePos. Otherwise _FlushBuffer() and reset.
834 if (offset >= _bufferPos && offset <= (_bufferPos + _buffer.size)) {
835 _filePos = offset;
836 }
837 else {
838 _FlushBuffer();
839 _bufferPos = _filePos = offset;
840 }
841 }
842
843 // Seek to the next position that's a multiple of \p alignment. Alignment
844 // must be a power-of-two.
Align(int alignment)845 inline int64_t Align(int alignment) {
846 Seek((Tell() + alignment - 1) & ~(alignment - 1));
847 return Tell();
848 }
849
850 private:
_FlushBuffer()851 inline void _FlushBuffer() {
852 if (_buffer.size) {
853 // Queue a write of _buffer bytes to the file at _bufferPos. Set
854 // _bufferPos to be _filePos.
855 _QueueWrite(std::move(_buffer), _bufferPos);
856 // Get a new _buffer. May have to wait if all are pending writes.
857 while (!_freeBuffers.try_pop(_buffer))
858 _dispatcher.Wait();
859 }
860 // Adjust the buffer to start at the write head.
861 _bufferPos = _filePos;
862 }
863
_WriteToBuffer(void const * bytes,int64_t nBytes)864 inline void _WriteToBuffer(void const *bytes, int64_t nBytes) {
865 // Fill the buffer, update its size and update the write head. Caller
866 // guarantees no overrun.
867 int64_t writeStart = (_filePos - _bufferPos);
868 if (writeStart + nBytes > _buffer.size) {
869 _buffer.size = writeStart + nBytes;
870 }
871 void *bufPtr = static_cast<void *>(_buffer.bytes.get() + writeStart);
872 memcpy(bufPtr, bytes, nBytes);
873 _filePos += nBytes;
874 }
875
876 // Move-only write operation for the writer task to process.
877 struct _WriteOp {
878 _WriteOp() = default;
879 _WriteOp(_WriteOp const &) = delete;
880 _WriteOp(_WriteOp &&) = default;
881 _WriteOp &operator=(_WriteOp &&) = default;
_WriteOpUsd_CrateFile::CrateFile::_BufferedOutput::_WriteOp882 _WriteOp(_Buffer &&buf, int64_t pos) : buf(std::move(buf)), pos(pos) {}
883 _Buffer buf;
884 int64_t pos = 0;
885 };
886
_QueueWrite(_Buffer && buf,int64_t pos)887 inline int64_t _QueueWrite(_Buffer &&buf, int64_t pos) {
888 // Arrange to write the buffered data. Enqueue the op and wake the
889 // writer task.
890 int64_t sz = static_cast<int64_t>(buf.size);
891 _writeQueue.push(_WriteOp(std::move(buf), pos));
892 _writeTask.Wake();
893 return sz;
894 }
895
_DoWrites()896 void _DoWrites() {
897 // This is the writer task. It just pops off ops and writes them, then
898 // moves the buffer to the free list.
899 _WriteOp op;
900 while (_writeQueue.try_pop(op)) {
901 // Write the bytes.
902 WriteToAsset(_file, op.buf.bytes.get(), op.buf.size, op.pos);
903 // Add the buffer back to _freeBuffers for reuse.
904 op.buf.size = 0;
905 _freeBuffers.push(std::move(op.buf));
906 }
907 }
908
909 // Write head in the file. Always inside the buffer region.
910 int64_t _filePos;
911 OutputType _file;
912
913 // Start of current buffer is at this file offset.
914 int64_t _bufferPos;
915 _Buffer _buffer;
916
917 // Queue of free buffer objects.
918 tbb::concurrent_queue<_Buffer> _freeBuffers;
919 // Queue of pending write operations.
920 tbb::concurrent_queue<_WriteOp> _writeQueue;
921
922 WorkDispatcher _dispatcher;
923 WorkSingularTask _writeTask;
924 };
925
926 ////////////////////////////////////////////////////////////////////////
927 // _PackingContext
928 struct CrateFile::_PackingContext
929 {
930 #if AR_VERSION == 1
931 using OutputType = TfSafeOutputFile;
_GetUsd_CrateFile::CrateFile::_PackingContext932 static FILE* _Get(OutputType& out) { return out.Get(); }
933 #else
934 using OutputType = ArWritableAssetSharedPtr;
935 static ArWritableAsset* _Get(OutputType& out) { return out.get(); }
936 #endif
937
938 _PackingContext() = delete;
939 _PackingContext(_PackingContext const &) = delete;
940 _PackingContext &operator=(_PackingContext const &) = delete;
941
_PackingContextUsd_CrateFile::CrateFile::_PackingContext942 _PackingContext(CrateFile *crate,
943 OutputType &&outAsset,
944 std::string const &fileName)
945 : fileName(fileName)
946 , writeVersion(crate->_assetPath.empty() ?
947 GetVersionForNewlyCreatedFiles() :
948 Version(crate->_boot))
949 , bufferedOutput(_Get(outAsset))
950 , outputAsset(std::move(outAsset)) {
951
952 // Populate this context with everything we need from \p crate in order
953 // to do deduplication, etc.
954 WorkWithScopedParallelism([this, crate]() {
955 WorkDispatcher wd;
956
957 // Read in any unknown sections so we can rewrite them later.
958 wd.Run([this, crate]() {
959 for (auto const &sec: crate->_toc.sections) {
960 if (!_IsKnownSection(sec.name)) {
961 unknownSections.emplace_back(
962 sec.name, _ReadSectionBytes(sec, crate),
963 sec.size);
964 }
965 }
966 });
967
968 // Ensure that pathToPathIndex is correctly populated.
969 wd.Run([this, crate]() {
970 for (size_t i = 0; i != crate->_paths.size(); ++i)
971 pathToPathIndex[crate->_paths[i]] = PathIndex(i);
972 });
973
974 // Ensure that fieldToFieldIndex is correctly populated.
975 wd.Run([this, crate]() {
976 for (size_t i = 0; i != crate->_fields.size(); ++i)
977 fieldToFieldIndex[
978 crate->_fields[i]] = FieldIndex(i);
979 });
980
981 // Ensure that fieldsToFieldSetIndex is correctly populated.
982 auto const &fsets = crate->_fieldSets;
983 wd.Run([this, &fsets]() {
984 vector<FieldIndex> fieldIndexes;
985 for (auto fsBegin = fsets.begin(),
986 fsEnd = find(
987 fsBegin, fsets.end(), FieldIndex());
988 fsBegin != fsets.end();
989 fsBegin = fsEnd + 1,
990 fsEnd = find(
991 fsBegin, fsets.end(), FieldIndex())) {
992 fieldIndexes.assign(fsBegin, fsEnd);
993 fieldsToFieldSetIndex[fieldIndexes] =
994 FieldSetIndex(fsBegin - fsets.begin());
995 }
996 });
997
998 // Ensure that tokenToTokenIndex is correctly populated.
999 wd.Run([this, crate]() {
1000 for (size_t i = 0; i != crate->_tokens.size(); ++i)
1001 tokenToTokenIndex[
1002 crate->_tokens[i]] = TokenIndex(i);
1003 });
1004
1005 // Ensure that stringToStringIndex is correctly populated.
1006 wd.Run([this, crate]() {
1007 for (size_t i = 0; i != crate->_strings.size(); ++i)
1008 stringToStringIndex[
1009 crate->GetString(StringIndex(i))] =
1010 StringIndex(i);
1011 });
1012 });
1013
1014 // Set file pos to start of the structural sections in the current TOC.
1015 bufferedOutput.Seek(crate->_toc.GetMinimumSectionStart());
1016 }
1017
1018 #if AR_VERSION == 1
1019 // Destructively move the output file out of this context.
ExtractOutputFileUsd_CrateFile::CrateFile::_PackingContext1020 TfSafeOutputFile ExtractOutputFile() {
1021 return std::move(outputAsset);
1022 }
1023 #else
1024 // Close output asset. No further writes may be done.
CloseOutputAssetUsd_CrateFile::CrateFile::_PackingContext1025 bool CloseOutputAsset() {
1026 return outputAsset->Close();
1027 }
1028 #endif
1029
1030 // Inform the writer that the output stream requires the given version
1031 // (or newer) to be read back. This allows the writer to start with
1032 // a conservative version assumption and promote to newer versions
1033 // only as required by the data stream contents.
RequestWriteVersionUpgradeUsd_CrateFile::CrateFile::_PackingContext1034 bool RequestWriteVersionUpgrade(Version ver, std::string reason) {
1035 if (!writeVersion.CanRead(ver)) {
1036 TF_WARN("Upgrading crate file <%s> from version %s to %s: %s",
1037 fileName.c_str(),
1038 writeVersion.AsString().c_str(), ver.AsString().c_str(),
1039 reason.c_str());
1040 writeVersion = ver;
1041 }
1042 // For now, this always returns true, indicating success. In
1043 // the future, we anticipate a mechanism to confirm the upgrade
1044 // is desired -- in which case this could return true or false.
1045 return true;
1046 }
1047
1048 // Read the bytes of some unknown section into memory so we can rewrite them
1049 // out later (to preserve it).
1050 RawDataPtr
_ReadSectionBytesUsd_CrateFile::CrateFile::_PackingContext1051 _ReadSectionBytes(_Section const &sec, CrateFile *crate) const {
1052 RawDataPtr result(new char[sec.size]);
1053 crate->_ReadRawBytes(sec.start, sec.size, result.get());
1054 return result;
1055 }
1056
1057 // Deduplication tables.
1058 unordered_map<TfToken, TokenIndex, _Hasher> tokenToTokenIndex;
1059 unordered_map<string, StringIndex, _Hasher> stringToStringIndex;
1060 unordered_map<SdfPath, PathIndex, SdfPath::Hash> pathToPathIndex;
1061 unordered_map<Field, FieldIndex, _Hasher> fieldToFieldIndex;
1062
1063 // A mapping from a group of fields to their starting index in _fieldSets.
1064 unordered_map<vector<FieldIndex>,
1065 FieldSetIndex, _Hasher> fieldsToFieldSetIndex;
1066
1067 // Unknown sections we're moving to the new structural area.
1068 vector<tuple<string, RawDataPtr, size_t>> unknownSections;
1069
1070 // Filename we're writing to.
1071 std::string fileName;
1072 // Version we're writing.
1073 Version writeVersion;
1074 // BufferedOutput helper.
1075 _BufferedOutput bufferedOutput;
1076 // Output destination.
1077 OutputType outputAsset;
1078 };
1079
1080 /////////////////////////////////////////////////////////////////////////
1081 // Readers
1082 class CrateFile::_ReaderBase
1083 {
1084 public:
_ReaderBase(CrateFile const * crate)1085 _ReaderBase(CrateFile const *crate) : crate(crate) {}
1086
1087 template <class T>
GetUninlinedValue(uint32_t x,T *) const1088 T GetUninlinedValue(uint32_t x, T *) const {
1089 static_assert(sizeof(T) <= sizeof(x), "");
1090 T r;
1091 memcpy(&r, &x, sizeof(r));
1092 return r;
1093 }
1094
GetUninlinedValue(uint32_t i,string *) const1095 string const & GetUninlinedValue(uint32_t i, string *) const {
1096 return crate->GetString(StringIndex(i));
1097 }
1098
GetUninlinedValue(uint32_t i,TfToken *) const1099 TfToken const &GetUninlinedValue(uint32_t i, TfToken *) const {
1100 return crate->GetToken(TokenIndex(i));
1101 }
1102
GetUninlinedValue(uint32_t i,SdfPath *) const1103 SdfPath const &GetUninlinedValue(uint32_t i, SdfPath *) const {
1104 return crate->GetPath(PathIndex(i));
1105 }
1106
GetUninlinedValue(uint32_t i,SdfAssetPath *) const1107 SdfAssetPath GetUninlinedValue(uint32_t i, SdfAssetPath *) const {
1108 return SdfAssetPath(crate->GetToken(TokenIndex(i)));
1109 }
1110
GetUninlinedValue(uint32_t i,SdfVariability *) const1111 SdfVariability GetUninlinedValue(uint32_t i, SdfVariability *) const {
1112 // Explicitly convert legacy SdfVariabilityConfig value to
1113 // SdfVariabilityUniform. This "config" variability was never used
1114 // in USD but clients may have written this value out so we need
1115 // to handle it to maintain backwards compatibility.
1116 static const uint32_t LEGACY_CONFIG_VARIABILITY = 2;
1117 if (i == LEGACY_CONFIG_VARIABILITY) {
1118 return SdfVariabilityUniform;
1119 }
1120 return static_cast<SdfVariability>(i);
1121 }
1122
1123 CrateFile const *crate;
1124 };
1125
1126 template <class ByteStream>
1127 class CrateFile::_Reader : public _ReaderBase
1128 {
_RecursiveRead()1129 void _RecursiveRead() {
1130 auto start = src.Tell();
1131 auto offset = Read<int64_t>();
1132 src.Seek(start + offset);
1133 }
1134
_RecursiveReadAndPrefetch()1135 void _RecursiveReadAndPrefetch() {
1136 auto start = src.Tell();
1137 auto offset = Read<int64_t>();
1138 src.Prefetch(start, offset);
1139 src.Seek(start + offset);
1140 }
1141
1142 public:
1143 static constexpr bool StreamSupportsZeroCopy = ByteStream::SupportsZeroCopy;
1144
_Reader(CrateFile const * crate,ByteStream & src)1145 _Reader(CrateFile const *crate, ByteStream &src)
1146 : _ReaderBase(crate)
1147 , src(src) {}
1148
1149 template <class T>
1150 static typename std::enable_if<_IsBitwiseReadWrite<T>::value, T>::type
StaticRead(ByteStream & src,T *)1151 StaticRead(ByteStream &src, T *) {
1152 T bits;
1153 src.Read(&bits, sizeof(bits));
1154 return bits;
1155 }
1156
Prefetch(int64_t offset,int64_t size)1157 void Prefetch(int64_t offset, int64_t size) { src.Prefetch(offset, size); }
1158
Seek(uint64_t offset)1159 void Seek(uint64_t offset) { src.Seek(offset); }
1160
1161 // Map helper.
1162 template <class Map>
ReadMap()1163 Map ReadMap() {
1164 Map map;
1165 auto sz = Read<uint64_t>();
1166 while (sz--) {
1167 // Do not combine the following into one statement. It must be
1168 // separate because the two modifications to 'src' must be correctly
1169 // sequenced.
1170 auto key = Read<typename Map::key_type>();
1171 map[key] = Read<typename Map::mapped_type>();
1172 }
1173 return map;
1174 }
1175
1176 ////////////////////////////////////////////////////////////////////////
1177 // Base template for Read. It dispatches to the overloads that take a
1178 // dummy pointer argument to allow overloading/enable_if.
1179 template <class T>
Read()1180 inline T Read() {
1181 return this->Read(static_cast<T *>(nullptr));
1182 }
1183
1184 // read bitwise.
1185 template <class T>
1186 typename std::enable_if<_IsBitwiseReadWrite<T>::value, T>::type
Read(T *)1187 Read(T *) { return StaticRead(src, static_cast<T *>(nullptr)); }
1188
Read(_TableOfContents *)1189 _TableOfContents Read(_TableOfContents *) {
1190 _TableOfContents ret;
1191 ret.sections = Read<decltype(ret.sections)>();
1192 return ret;
1193 }
Read(string *)1194 string Read(string *) { return crate->GetString(Read<StringIndex>()); }
Read(TfToken *)1195 TfToken Read(TfToken *) { return crate->GetToken(Read<TokenIndex>()); }
Read(SdfPath *)1196 SdfPath Read(SdfPath *) { return crate->GetPath(Read<PathIndex>()); }
Read(VtDictionary *)1197 VtDictionary Read(VtDictionary *) { return ReadMap<VtDictionary>(); }
Read(SdfAssetPath *)1198 SdfAssetPath Read(SdfAssetPath *) {
1199 return SdfAssetPath(Read<string>());
1200 }
Read(SdfTimeCode *)1201 SdfTimeCode Read(SdfTimeCode *) { return SdfTimeCode(Read<double>()); }
Read(SdfUnregisteredValue *)1202 SdfUnregisteredValue Read(SdfUnregisteredValue *) {
1203 VtValue val = Read<VtValue>();
1204 if (val.IsHolding<string>())
1205 return SdfUnregisteredValue(val.UncheckedGet<string>());
1206 if (val.IsHolding<VtDictionary>())
1207 return SdfUnregisteredValue(val.UncheckedGet<VtDictionary>());
1208 if (val.IsHolding<SdfUnregisteredValueListOp>())
1209 return SdfUnregisteredValue(
1210 val.UncheckedGet<SdfUnregisteredValueListOp>());
1211 TF_CODING_ERROR("SdfUnregisteredValue in crate file contains invalid "
1212 "type '%s' = '%s'; expected string, VtDictionary or "
1213 "SdfUnregisteredValueListOp; returning empty",
1214 val.GetTypeName().c_str(), TfStringify(val).c_str());
1215 return SdfUnregisteredValue();
1216 }
Read(SdfVariantSelectionMap *)1217 SdfVariantSelectionMap Read(SdfVariantSelectionMap *) {
1218 return ReadMap<SdfVariantSelectionMap>();
1219 }
Read(SdfLayerOffset *)1220 SdfLayerOffset Read(SdfLayerOffset *) {
1221 // Do not combine the following into one statement. It must be separate
1222 // because the two modifications to 'src' must be correctly sequenced.
1223 auto offset = Read<double>();
1224 auto scale = Read<double>();
1225 return SdfLayerOffset(offset, scale);
1226 }
Read(SdfReference *)1227 SdfReference Read(SdfReference *) {
1228 // Do not combine the following into one statement. It must be separate
1229 // because the two modifications to 'src' must be correctly sequenced.
1230 auto assetPath = Read<std::string>();
1231 auto primPath = Read<SdfPath>();
1232 auto layerOffset = Read<SdfLayerOffset>();
1233 auto customData = Read<VtDictionary>();
1234 return SdfReference(std::move(assetPath), std::move(primPath),
1235 std::move(layerOffset), std::move(customData));
1236 }
Read(SdfPayload *)1237 SdfPayload Read(SdfPayload *) {
1238 // Do not combine the following into one statement. It must be separate
1239 // because the two modifications to 'src' must be correctly sequenced.
1240 auto assetPath = Read<string>();
1241 auto primPath = Read<SdfPath>();
1242
1243 // Layer offsets were added to SdfPayload starting in 0.8.0. Files
1244 // before that cannot have them.
1245 const bool canReadLayerOffset =
1246 (Version(crate->_boot) >= Version(0, 8, 0));
1247 if (canReadLayerOffset) {
1248 auto layerOffset = Read<SdfLayerOffset>();
1249 return SdfPayload(assetPath, primPath, layerOffset);
1250 } else {
1251 return SdfPayload(assetPath, primPath);
1252 }
1253 }
1254 template <class T>
Read(SdfListOp<T> *)1255 SdfListOp<T> Read(SdfListOp<T> *) {
1256 SdfListOp<T> listOp;
1257 auto h = Read<_ListOpHeader>();
1258 if (h.IsExplicit()) { listOp.ClearAndMakeExplicit(); }
1259 if (h.HasExplicitItems()) {
1260 listOp.SetExplicitItems(Read<vector<T>>()); }
1261 if (h.HasAddedItems()) { listOp.SetAddedItems(Read<vector<T>>()); }
1262 if (h.HasPrependedItems()) {
1263 listOp.SetPrependedItems(Read<vector<T>>()); }
1264 if (h.HasAppendedItems()) {
1265 listOp.SetAppendedItems(Read<vector<T>>()); }
1266 if (h.HasDeletedItems()) { listOp.SetDeletedItems(Read<vector<T>>()); }
1267 if (h.HasOrderedItems()) { listOp.SetOrderedItems(Read<vector<T>>()); }
1268 return listOp;
1269 }
Read(VtValue *)1270 VtValue Read(VtValue *) {
1271 _RecursiveReadAndPrefetch();
1272 auto rep = Read<ValueRep>();
1273 return crate->UnpackValue(rep);
1274 }
1275
Read(TimeSamples *)1276 TimeSamples Read(TimeSamples *) {
1277
1278 TimeSamples ret;
1279
1280 // Reconstitute a rep for this very location in the file to be retained
1281 // in the TimeSamples result.
1282 ret.valueRep = ValueRepFor<TimeSamples>(src.Tell());
1283
1284 _RecursiveRead();
1285 auto timesRep = Read<ValueRep>();
1286
1287 // Deduplicate times in-memory by ValueRep.
1288 // Optimistically take the read lock and see if we already have times.
1289 tbb::spin_rw_mutex::scoped_lock
1290 lock(crate->_sharedTimesMutex, /*write=*/false);
1291 auto sharedTimesIter = crate->_sharedTimes.find(timesRep);
1292 if (sharedTimesIter != crate->_sharedTimes.end()) {
1293 // Yes, reuse existing times.
1294 ret.times = sharedTimesIter->second;
1295 } else {
1296 // The lock upgrade here may or may not be atomic. This means
1297 // someone else may have populated the table while we were
1298 // upgrading.
1299 lock.upgrade_to_writer();
1300 auto iresult =
1301 crate->_sharedTimes.emplace(timesRep, Usd_EmptySharedTag);
1302 if (iresult.second) {
1303 // We get to do the population.
1304 auto sharedTimes = TimeSamples::SharedTimes();
1305 crate->_UnpackValue(timesRep, &sharedTimes.GetMutable());
1306 iresult.first->second.swap(sharedTimes);
1307 }
1308 ret.times = iresult.first->second;
1309 }
1310 lock.release();
1311
1312 _RecursiveRead();
1313
1314 // Store the offset to the value reps in the file. The values are
1315 // encoded as a uint64_t size followed by contiguous reps. So we jump
1316 // over that uint64_t and store the start of the reps. Then we seek
1317 // forward past the reps to continue.
1318 auto numValues = Read<uint64_t>();
1319 ret.valuesFileOffset = src.Tell();
1320
1321 // Now move past the reps to continue.
1322 src.Seek(ret.valuesFileOffset + numValues * sizeof(ValueRep));
1323
1324 return ret;
1325 }
1326
1327 template <class T>
Read(vector<T> *)1328 vector<T> Read(vector<T> *) {
1329 auto sz = Read<uint64_t>();
1330 vector<T> vec(sz);
1331 ReadContiguous(vec.data(), sz);
1332 return vec;
1333 }
1334
1335 template <class T>
1336 typename std::enable_if<_IsBitwiseReadWrite<T>::value>::type
ReadContiguous(T * values,size_t sz)1337 ReadContiguous(T *values, size_t sz) {
1338 src.Read(static_cast<void *>(values), sz * sizeof(*values));
1339 }
1340
1341 template <class T>
1342 typename std::enable_if<!_IsBitwiseReadWrite<T>::value>::type
ReadContiguous(T * values,size_t sz)1343 ReadContiguous(T *values, size_t sz) {
1344 std::for_each(values, values + sz, [this](T &v) { v = Read<T>(); });
1345 }
1346
1347 ByteStream src;
1348 };
1349
1350 template <class ByteStream>
1351 CrateFile::_Reader<ByteStream>
_MakeReader(ByteStream src) const1352 CrateFile::_MakeReader(ByteStream src) const
1353 {
1354 return _Reader<ByteStream>(this, src);
1355 }
1356
1357 /////////////////////////////////////////////////////////////////////////
1358 // Writers
1359 class CrateFile::_Writer
1360 {
1361 public:
_Writer(CrateFile * crate)1362 explicit _Writer(CrateFile *crate)
1363 : crate(crate)
1364 , sink(&crate->_packCtx->bufferedOutput) {}
1365
1366 // Recursive write helper. We use these when writing values if we may
1367 // invoke _PackValue() recursively. Since _PackValue() may or may not write
1368 // to the file, we need to account for jumping over that written nested
1369 // data, and this function automates that.
1370 template <class Fn>
_RecursiveWrite(Fn const & fn)1371 void _RecursiveWrite(Fn const &fn) {
1372 // Reserve space for a forward offset to where the primary data will
1373 // live.
1374 int64_t offsetLoc = Tell();
1375 WriteAs<int64_t>(0);
1376 // Invoke the writing function, which may write arbitrary data.
1377 fn();
1378 // Now that we know where the primary data will end up, seek back and
1379 // write the offset value, then seek forward again.
1380 int64_t end = Tell();
1381 Seek(offsetLoc);
1382 WriteAs<int64_t>(end - offsetLoc);
1383 Seek(end);
1384 }
1385
1386 public:
1387
Tell() const1388 int64_t Tell() const { return sink->Tell(); }
Seek(int64_t offset)1389 void Seek(int64_t offset) { sink->Seek(offset); }
Flush()1390 void Flush() { sink->Flush(); }
Align(int alignment)1391 int64_t Align(int alignment) { return sink->Align(alignment); }
1392
1393 template <class T>
GetInlinedValue(T x)1394 uint32_t GetInlinedValue(T x) {
1395 uint32_t r = 0;
1396 static_assert(sizeof(x) <= sizeof(r), "");
1397 memcpy(&r, &x, sizeof(x));
1398 return r;
1399 }
1400
GetInlinedValue(string const & s)1401 uint32_t GetInlinedValue(string const &s) {
1402 return crate->_AddString(s).value;
1403 }
1404
GetInlinedValue(TfToken const & t)1405 uint32_t GetInlinedValue(TfToken const &t) {
1406 return crate->_AddToken(t).value;
1407 }
1408
GetInlinedValue(SdfPath const & p)1409 uint32_t GetInlinedValue(SdfPath const &p) {
1410 return crate->_AddPath(p).value;
1411 }
1412
GetInlinedValue(SdfAssetPath const & p)1413 uint32_t GetInlinedValue(SdfAssetPath const &p) {
1414 return crate->_AddToken(TfToken(p.GetAssetPath())).value;
1415 }
1416
1417 ////////////////////////////////////////////////////////////////////////
1418 // Basic Write
1419 template <class T>
1420 typename std::enable_if<_IsBitwiseReadWrite<T>::value>::type
Write(T const & bits)1421 Write(T const &bits) { sink->Write(&bits, sizeof(bits)); }
1422
1423 template <class U, class T>
WriteAs(T const & obj)1424 void WriteAs(T const &obj) { return Write(static_cast<U>(obj)); }
1425
1426 // Map helper.
1427 template <class Map>
WriteMap(Map const & map)1428 void WriteMap(Map const &map) {
1429 WriteAs<uint64_t>(map.size());
1430 for (auto const &kv: map) {
1431 Write(kv.first);
1432 Write(kv.second);
1433 }
1434 }
1435
Write(_TableOfContents const & toc)1436 void Write(_TableOfContents const &toc) { Write(toc.sections); }
Write(std::string const & str)1437 void Write(std::string const &str) { Write(crate->_AddString(str)); }
Write(TfToken const & tok)1438 void Write(TfToken const &tok) { Write(crate->_AddToken(tok)); }
Write(SdfPath const & path)1439 void Write(SdfPath const &path) { Write(crate->_AddPath(path)); }
Write(VtDictionary const & dict)1440 void Write(VtDictionary const &dict) { WriteMap(dict); }
Write(SdfAssetPath const & ap)1441 void Write(SdfAssetPath const &ap) { Write(ap.GetAssetPath()); }
Write(SdfTimeCode const & tc)1442 void Write(SdfTimeCode const &tc) {
1443 crate->_packCtx->RequestWriteVersionUpgrade(
1444 Version(0, 9, 0),
1445 "A timecode or timecode[] value type was detected, which requires "
1446 "crate version 0.9.0.");
1447 Write(tc.GetValue());
1448 }
Write(SdfUnregisteredValue const & urv)1449 void Write(SdfUnregisteredValue const &urv) { Write(urv.GetValue()); }
Write(SdfVariantSelectionMap const & vsmap)1450 void Write(SdfVariantSelectionMap const &vsmap) { WriteMap(vsmap); }
Write(SdfLayerOffset const & layerOffset)1451 void Write(SdfLayerOffset const &layerOffset) {
1452 Write(layerOffset.GetOffset());
1453 Write(layerOffset.GetScale());
1454 }
Write(SdfReference const & ref)1455 void Write(SdfReference const &ref) {
1456 Write(ref.GetAssetPath());
1457 Write(ref.GetPrimPath());
1458 Write(ref.GetLayerOffset());
1459 Write(ref.GetCustomData());
1460 }
Write(SdfPayload const & ref)1461 void Write(SdfPayload const &ref) {
1462 // Layer offsets in payloads are only supported in version 0.8 and
1463 // later. If we have to write one, we may have to upgrade the version
1464 if (!ref.GetLayerOffset().IsIdentity()) {
1465 crate->_packCtx->RequestWriteVersionUpgrade(
1466 Version(0, 8, 0),
1467 "A payload with a non-identity layer offset "
1468 "was detected, which requires crate version 0.8.0.");
1469 }
1470 Write(ref.GetAssetPath());
1471 Write(ref.GetPrimPath());
1472
1473 // Always write layer offsets in files versioned 0.8.0 or later
1474 if (crate->_packCtx->writeVersion >= Version(0, 8, 0)) {
1475 Write(ref.GetLayerOffset());
1476 }
1477 }
1478 template <class T>
Write(SdfListOp<T> const & listOp)1479 void Write(SdfListOp<T> const &listOp) {
1480 _ListOpHeader h(listOp);
1481 if (h.HasPrependedItems() || h.HasAppendedItems()) {
1482 crate->_packCtx->RequestWriteVersionUpgrade(
1483 Version(0, 2, 0),
1484 "A SdfListOp value using a prepended or appended value "
1485 "was detected, which requires crate version 0.2.0.");
1486 }
1487 Write(h);
1488 if (h.HasExplicitItems()) { Write(listOp.GetExplicitItems()); }
1489 if (h.HasAddedItems()) { Write(listOp.GetAddedItems()); }
1490 if (h.HasPrependedItems()) { Write(listOp.GetPrependedItems()); }
1491 if (h.HasAppendedItems()) { Write(listOp.GetAppendedItems()); }
1492 if (h.HasDeletedItems()) { Write(listOp.GetDeletedItems()); }
1493 if (h.HasOrderedItems()) { Write(listOp.GetOrderedItems()); }
1494 }
1495 // Specialized override for payload list ops which require a version check.
Write(SdfPayloadListOp const & listOp)1496 void Write(SdfPayloadListOp const &listOp) {
1497 crate->_packCtx->RequestWriteVersionUpgrade(
1498 Version(0, 8, 0),
1499 "A SdfPayloadListOp value was detected which requires crate "
1500 "version 0.8.0.");
1501 Write<SdfPayload>(listOp);
1502 }
Write(VtValue const & val)1503 void Write(VtValue const &val) {
1504 ValueRep rep;
1505 _RecursiveWrite(
1506 [this, &val, &rep]() { rep = crate->_PackValue(val); });
1507 Write(rep);
1508 }
1509
Write(TimeSamples const & samples)1510 void Write(TimeSamples const &samples) {
1511 // Pack the times to deduplicate.
1512 ValueRep timesRep;
1513 _RecursiveWrite([this, ×Rep, &samples]() {
1514 timesRep = crate->_PackValue(samples.times.Get());
1515 });
1516 Write(timesRep);
1517
1518 // Pack the individual elements, to deduplicate them.
1519 vector<ValueRep> reps(samples.values.size());
1520 _RecursiveWrite([this, &reps, &samples]() {
1521 transform(samples.values.begin(), samples.values.end(),
1522 reps.begin(),
1523 [this](VtValue const &val) {
1524 return crate->_PackValue(val);
1525 });
1526 });
1527
1528 // Write size and contiguous reps.
1529 WriteAs<uint64_t>(reps.size());
1530 WriteContiguous(reps.data(), reps.size());
1531 }
1532
1533 template <class T>
Write(vector<T> const & vec)1534 void Write(vector<T> const &vec) {
1535 WriteAs<uint64_t>(vec.size());
1536 WriteContiguous(vec.data(), vec.size());
1537 }
1538
1539 template <class T>
1540 typename std::enable_if<_IsBitwiseReadWrite<T>::value>::type
WriteContiguous(T const * values,size_t sz)1541 WriteContiguous(T const *values, size_t sz) {
1542 sink->Write(values, sizeof(*values) * sz);
1543 }
1544
1545 template <class T>
1546 typename std::enable_if<!_IsBitwiseReadWrite<T>::value>::type
WriteContiguous(T const * values,size_t sz)1547 WriteContiguous(T const *values, size_t sz) {
1548 std::for_each(values, values + sz, [this](T const &v) { Write(v); });
1549 }
1550
1551 CrateFile *crate;
1552 _BufferedOutput *sink;
1553 };
1554
1555
1556 ////////////////////////////////////////////////////////////////////////
1557 // ValueHandler class hierarchy. See comment for _ValueHandler itself for more
1558 // information.
1559
1560 struct CrateFile::_ValueHandlerBase {
1561 // Base Clear() does nothing.
ClearUsd_CrateFile::CrateFile::_ValueHandlerBase1562 void Clear() {}
1563 };
1564
1565 // Scalar handler for non-inlined types -- does deduplication.
1566 template <class T, class Enable>
1567 struct CrateFile::_ScalarValueHandlerBase : _ValueHandlerBase
1568 {
PackUsd_CrateFile::CrateFile::_ScalarValueHandlerBase1569 inline ValueRep Pack(_Writer writer, T const &val) {
1570 // See if we can inline the value -- we might be able to if there's some
1571 // encoding that can exactly represent it in 4 bytes.
1572 uint32_t ival = 0;
1573 if (_EncodeInline(val, &ival)) {
1574 auto ret = ValueRepFor<T>(ival);
1575 ret.SetIsInlined();
1576 return ret;
1577 }
1578
1579 // Otherwise dedup and/or write...
1580 if (!_valueDedup) {
1581 _valueDedup.reset(
1582 new typename decltype(_valueDedup)::element_type);
1583 }
1584
1585 auto iresult = _valueDedup->emplace(val, ValueRep());
1586 ValueRep &target = iresult.first->second;
1587 if (iresult.second) {
1588 // Not yet present. Invoke the write function.
1589 target = ValueRepFor<T>(writer.Tell());
1590 writer.Write(val);
1591 }
1592 return target;
1593 }
1594 template <class Reader>
UnpackUsd_CrateFile::CrateFile::_ScalarValueHandlerBase1595 inline void Unpack(Reader reader, ValueRep rep, T *out) const {
1596 // If the value is inlined, just decode it.
1597 if (rep.IsInlined()) {
1598 uint32_t tmp = (rep.GetPayload() &
1599 ((1ull << (sizeof(uint32_t) * 8))-1));
1600 _DecodeInline(out, tmp);
1601 return;
1602 }
1603 // Otherwise we have to read it from the file.
1604 reader.Seek(rep.GetPayload());
1605 *out = reader.template Read<T>();
1606 }
ClearUsd_CrateFile::CrateFile::_ScalarValueHandlerBase1607 void Clear() {
1608 _valueDedup.reset();
1609 }
1610 std::unique_ptr<std::unordered_map<T, ValueRep, _Hasher>> _valueDedup;
1611 };
1612
1613 // Scalar handler for inlined types -- no deduplication.
1614 template <class T>
1615 struct CrateFile::_ScalarValueHandlerBase<
1616 T, typename std::enable_if<ValueTypeTraits<T>::isInlined>::type>
1617 : _ValueHandlerBase
1618 {
PackUsd_CrateFile::CrateFile::_ScalarValueHandlerBase1619 inline ValueRep Pack(_Writer writer, T val) {
1620 // Inline it into the rep.
1621 return ValueRepFor<T>(writer.GetInlinedValue(val));
1622 }
1623 template <class Reader>
UnpackUsd_CrateFile::CrateFile::_ScalarValueHandlerBase1624 inline void Unpack(Reader reader, ValueRep rep, T *out) const {
1625 // Value is directly in payload data.
1626 uint32_t tmp =
1627 (rep.GetPayload() & ((1ull << (sizeof(uint32_t) * 8))-1));
1628 *out = reader.GetUninlinedValue(tmp, static_cast<T *>(nullptr));
1629 }
1630 };
1631
1632 // Array handler for types that don't support arrays.
1633 template <class T, class Enable>
1634 struct CrateFile::_ArrayValueHandlerBase : _ScalarValueHandlerBase<T>
1635 {
PackVtValueUsd_CrateFile::CrateFile::_ArrayValueHandlerBase1636 ValueRep PackVtValue(_Writer w, VtValue const &v) {
1637 return this->Pack(w, v.UncheckedGet<T>());
1638 }
1639
1640 template <class Reader>
UnpackVtValueUsd_CrateFile::CrateFile::_ArrayValueHandlerBase1641 void UnpackVtValue(Reader r, ValueRep rep, VtValue *out) {
1642 T obj;
1643 this->Unpack(r, rep, &obj);
1644 out->Swap(obj);
1645 }
1646 };
1647
1648 // Don't compress arrays smaller than this.
1649 constexpr size_t MinCompressedArraySize = 16;
1650
1651 template <class Writer, class T>
1652 static inline ValueRep
_WriteUncompressedArray(Writer w,VtArray<T> const & array,CrateFile::Version ver)1653 _WriteUncompressedArray(
1654 Writer w, VtArray<T> const &array, CrateFile::Version ver)
1655 {
1656 // We'll align the array to 8 bytes, so software can refer to mapped bytes
1657 // directly if possible.
1658 auto result = ValueRepForArray<T>(w.Align(sizeof(uint64_t)));
1659
1660 (ver < CrateFile::Version(0,7,0)) ?
1661 w.template WriteAs<uint32_t>(array.size()) :
1662 w.template WriteAs<uint64_t>(array.size());
1663
1664 w.WriteContiguous(array.cdata(), array.size());
1665
1666 return result;
1667 }
1668
1669 template <class Writer, class T>
1670 static inline ValueRep
_WritePossiblyCompressedArray(Writer w,VtArray<T> const & array,CrateFile::Version ver,...)1671 _WritePossiblyCompressedArray(
1672 Writer w, VtArray<T> const &array, CrateFile::Version ver, ...)
1673 {
1674 // Fallback case -- write uncompressed data.
1675 return _WriteUncompressedArray(w, array, ver);
1676 }
1677
1678 template <class Writer, class Int>
1679 static inline void
_WriteCompressedInts(Writer w,Int const * begin,size_t size)1680 _WriteCompressedInts(Writer w, Int const *begin, size_t size)
1681 {
1682 // Make a buffer to compress to, compress, and write.
1683 using Compressor = typename std::conditional<
1684 sizeof(Int) == 4,
1685 Usd_IntegerCompression,
1686 Usd_IntegerCompression64>::type;
1687 std::unique_ptr<char[]> compBuffer(
1688 new char[Compressor::GetCompressedBufferSize(size)]);
1689 size_t compSize =
1690 Compressor::CompressToBuffer(begin, size, compBuffer.get());
1691 w.template WriteAs<uint64_t>(compSize);
1692 w.WriteContiguous(compBuffer.get(), compSize);
1693 }
1694
1695 template <class Writer, class T>
1696 static inline
1697 typename std::enable_if<
1698 std::is_same<T, int>::value ||
1699 std::is_same<T, unsigned int>::value ||
1700 std::is_same<T, int64_t>::value ||
1701 std::is_same<T, uint64_t>::value,
1702 ValueRep>::type
_WritePossiblyCompressedArray(Writer w,VtArray<T> const & array,CrateFile::Version ver,int)1703 _WritePossiblyCompressedArray(
1704 Writer w, VtArray<T> const &array, CrateFile::Version ver, int)
1705 {
1706 auto result = ValueRepForArray<T>(w.Tell());
1707 // Total elements.
1708 (ver < CrateFile::Version(0,7,0)) ?
1709 w.template WriteAs<uint32_t>(array.size()) :
1710 w.template WriteAs<uint64_t>(array.size());
1711 if (array.size() < MinCompressedArraySize) {
1712 w.WriteContiguous(array.cdata(), array.size());
1713 } else {
1714 _WriteCompressedInts(w, array.cdata(), array.size());
1715 result.SetIsCompressed();
1716 }
1717 return result;
1718 }
1719
1720 template <class Writer, class T>
1721 static inline
1722 typename std::enable_if<
1723 std::is_same<T, GfHalf>::value ||
1724 std::is_same<T, float>::value ||
1725 std::is_same<T, double>::value,
1726 ValueRep>::type
_WritePossiblyCompressedArray(Writer w,VtArray<T> const & array,CrateFile::Version ver,int)1727 _WritePossiblyCompressedArray(
1728 Writer w, VtArray<T> const &array, CrateFile::Version ver, int)
1729 {
1730 // Version 0.6.0 introduced compressed floating point arrays.
1731 if (ver < CrateFile::Version(0,6,0) ||
1732 array.size() < MinCompressedArraySize) {
1733 return _WriteUncompressedArray(w, array, ver);
1734 }
1735
1736 // Check to see if all the floats are exactly represented as integers.
1737 auto isIntegral = [](T fp) {
1738 constexpr int32_t max = std::numeric_limits<int32_t>::max();
1739 constexpr int32_t min = std::numeric_limits<int32_t>::lowest();
1740 return min <= fp && fp <= max &&
1741 static_cast<T>(static_cast<int32_t>(fp)) == fp;
1742 };
1743 if (std::all_of(array.cdata(), array.cdata() + array.size(), isIntegral)) {
1744 // Encode as integers.
1745 auto result = ValueRepForArray<T>(w.Tell());
1746 (ver < CrateFile::Version(0,7,0)) ?
1747 w.template WriteAs<uint32_t>(array.size()) :
1748 w.template WriteAs<uint64_t>(array.size());
1749 result.SetIsCompressed();
1750 vector<int32_t> ints(array.size());
1751 std::copy(array.cdata(), array.cdata() + array.size(), ints.data());
1752 // Lowercase 'i' code indicates that the floats are written as
1753 // compressed ints.
1754 w.template WriteAs<int8_t>('i');
1755 _WriteCompressedInts(w, ints.data(), ints.size());
1756 return result;
1757 }
1758
1759 // Otherwise check if there are a small number of distinct values, which we
1760 // can then write as a lookup table and indexes into that table.
1761 vector<T> lut;
1762 // Ensure that we give up soon enough if it doesn't seem like building a
1763 // lookup table will be profitable. Check the first 1024 elements at most.
1764 unsigned int maxLutSize = std::min<size_t>(array.size() / 4, 1024);
1765 vector<uint32_t> indexes;
1766 for (auto elem: array) {
1767 auto iter = std::find(lut.begin(), lut.end(), elem);
1768 uint32_t index = iter-lut.begin();
1769 indexes.push_back(index);
1770 if (index == lut.size()) {
1771 if (lut.size() != maxLutSize) {
1772 lut.push_back(elem);
1773 } else {
1774 lut.clear();
1775 indexes.clear();
1776 break;
1777 }
1778 }
1779 }
1780 if (!lut.empty()) {
1781 // Use the lookup table. Lowercase 't' code indicates that floats are
1782 // written with a lookup table and indexes.
1783 auto result = ValueRepForArray<T>(w.Tell());
1784 (ver < CrateFile::Version(0,7,0)) ?
1785 w.template WriteAs<uint32_t>(array.size()) :
1786 w.template WriteAs<uint64_t>(array.size());
1787 result.SetIsCompressed();
1788 w.template WriteAs<int8_t>('t');
1789 // Write the lookup table itself.
1790 w.template WriteAs<uint32_t>(lut.size());
1791 w.WriteContiguous(lut.data(), lut.size());
1792 // Now write indexes.
1793 _WriteCompressedInts(w, indexes.data(), indexes.size());
1794 return result;
1795 }
1796
1797 // Otherwise, just write uncompressed floats. We don't need to write a code
1798 // byte here like the 'i' and 't' above since the resulting ValueRep is not
1799 // marked compressed -- the reader code will thus just read the uncompressed
1800 // values directly.
1801 return _WriteUncompressedArray(w, array, ver);
1802 }
1803
1804 template <class Reader, class T>
1805 static inline
1806 typename std::enable_if<!Reader::StreamSupportsZeroCopy ||
1807 !_IsBitwiseReadWrite<T>::value>::type
_ReadUncompressedArray(Reader reader,ValueRep rep,VtArray<T> * out,CrateFile::Version ver)1808 _ReadUncompressedArray(
1809 Reader reader, ValueRep rep, VtArray<T> *out, CrateFile::Version ver)
1810 {
1811 // The reader's bytestream does not support zero-copy, or the element type
1812 // is not bitwise identical in memory and on disk, so just read the contents
1813 // into memory.
1814 out->resize(
1815 ver < CrateFile::Version(0,7,0) ?
1816 reader.template Read<uint32_t>() :
1817 reader.template Read<uint64_t>());
1818 reader.ReadContiguous(out->data(), out->size());
1819 }
1820
1821 template <class Reader, class T>
1822 static inline
1823 typename std::enable_if<Reader::StreamSupportsZeroCopy &&
1824 _IsBitwiseReadWrite<T>::value>::type
_ReadUncompressedArray(Reader reader,ValueRep rep,VtArray<T> * out,CrateFile::Version ver)1825 _ReadUncompressedArray(
1826 Reader reader, ValueRep rep, VtArray<T> *out, CrateFile::Version ver)
1827 {
1828 static bool zeroCopyEnabled = TfGetEnvSetting(USDC_ENABLE_ZERO_COPY_ARRAYS);
1829
1830 // The reader's stream supports zero-copy and T is written to disk just as
1831 // it is represented in memory, so if the array is of reasonable size and
1832 // the memory is suitably aligned, then make an array that refers directly
1833 // into the stream's memory.
1834
1835 uint64_t size = (ver < CrateFile::Version(0,7,0)) ?
1836 reader.template Read<uint32_t>() :
1837 reader.template Read<uint64_t>();
1838
1839 // Check size and alignment -- the standard requires that alignments
1840 // are power-of-two.
1841 size_t numBytes = sizeof(T) * size;
1842 static constexpr size_t MinZeroCopyArrayBytes = 2048; // Half a page?
1843 if (zeroCopyEnabled &&
1844 /* size reasonable? */numBytes >= MinZeroCopyArrayBytes &&
1845 /* alignment ok? */
1846 (reinterpret_cast<uintptr_t>(
1847 reader.src.TellMemoryAddress()) & (alignof(T)-1)) == 0) {
1848
1849 // Make a VtArray with a foreign source that points into the stream. We
1850 // pass addRef=false here, because CreateZeroCopyDataSource does that
1851 // already -- it needs to know if it's taken the count from 0 to 1 or
1852 // not.
1853 void *addr = reader.src.TellMemoryAddress();
1854
1855 if (Vt_ArrayForeignDataSource *foreignSrc =
1856 reader.src.CreateZeroCopyDataSource(addr, numBytes)) {
1857 *out = VtArray<T>(
1858 foreignSrc, static_cast<T *>(addr), size, /*addRef=*/false);
1859 }
1860 else {
1861 // In case of error, return an empty array.
1862 out->clear();
1863 }
1864 }
1865 else {
1866 // Copy the data instead.
1867 out->resize(size);
1868 reader.ReadContiguous(out->data(), out->size());
1869 }
1870 }
1871
1872 template <class Reader, class T>
1873 static inline void
_ReadPossiblyCompressedArray(Reader reader,ValueRep rep,VtArray<T> * out,CrateFile::Version ver,...)1874 _ReadPossiblyCompressedArray(
1875 Reader reader, ValueRep rep, VtArray<T> *out, CrateFile::Version ver, ...)
1876 {
1877 // Fallback uncompressed case.
1878 _ReadUncompressedArray(reader, rep, out, ver);
1879 }
1880
1881 struct _CompressedIntsReader
1882 {
1883 template <class Reader, class Int>
ReadUsd_CrateFile::_CompressedIntsReader1884 void Read(Reader &reader, Int *out, size_t numInts) {
1885 using Compressor = typename std::conditional<
1886 sizeof(Int) == 4,
1887 Usd_IntegerCompression,
1888 Usd_IntegerCompression64>::type;
1889
1890 _AllocateBufferAndWorkingSpace<Compressor>(numInts);
1891 auto compressedSize = reader.template Read<uint64_t>();
1892 if (compressedSize > _compBufferSize) {
1893 // Don't read more than the available memory buffer.
1894 compressedSize = _compBufferSize;
1895 }
1896 reader.ReadContiguous(_compBuffer.get(), compressedSize);
1897 Compressor::DecompressFromBuffer(
1898 _compBuffer.get(), compressedSize, out, numInts,
1899 _workingSpace.get());
1900 }
1901
1902 private:
1903 template <class Comp>
_AllocateBufferAndWorkingSpaceUsd_CrateFile::_CompressedIntsReader1904 void _AllocateBufferAndWorkingSpace(size_t numInts) {
1905 size_t reqBufferSize = Comp::GetCompressedBufferSize(numInts);
1906 size_t reqWorkingSize = Comp::GetDecompressionWorkingSpaceSize(numInts);
1907 if (reqBufferSize > _compBufferSize) {
1908 _compBuffer.reset(new char[reqBufferSize]);
1909 _compBufferSize = reqBufferSize;
1910 }
1911 if (reqWorkingSize > _workingSpaceSize) {
1912 _workingSpace.reset(new char[reqWorkingSize]);
1913 _workingSpaceSize = reqWorkingSize;
1914 }
1915 }
1916
1917 std::unique_ptr<char[]> _compBuffer;
1918 size_t _compBufferSize = 0;
1919 std::unique_ptr<char[]> _workingSpace;
1920 size_t _workingSpaceSize = 0;
1921 };
1922
1923 template <class Reader, class Int>
1924 static inline void
_ReadCompressedInts(Reader & reader,Int * out,size_t size)1925 _ReadCompressedInts(Reader &reader, Int *out, size_t size)
1926 {
1927 _CompressedIntsReader r;
1928 r.Read(reader, out, size);
1929 }
1930
1931 template <class Reader, class T>
1932 static inline
1933 typename std::enable_if<
1934 std::is_same<T, int>::value ||
1935 std::is_same<T, unsigned int>::value ||
1936 std::is_same<T, int64_t>::value ||
1937 std::is_same<T, uint64_t>::value>::type
_ReadPossiblyCompressedArray(Reader reader,ValueRep rep,VtArray<T> * out,CrateFile::Version ver,int)1938 _ReadPossiblyCompressedArray(
1939 Reader reader, ValueRep rep, VtArray<T> *out, CrateFile::Version ver, int)
1940 {
1941 // Version 0.5.0 introduced compressed int arrays.
1942 if (ver < CrateFile::Version(0,5,0) || !rep.IsCompressed()) {
1943 _ReadUncompressedArray(reader, rep, out, ver);
1944 }
1945 else {
1946 // Read total elements.
1947 out->resize(ver < CrateFile::Version(0,7,0) ?
1948 reader.template Read<uint32_t>() :
1949 reader.template Read<uint64_t>());
1950 if (out->size() < MinCompressedArraySize) {
1951 reader.ReadContiguous(out->data(), out->size());
1952 } else {
1953 _ReadCompressedInts(reader, out->data(), out->size());
1954 }
1955 }
1956 }
1957
1958 template <class Reader, class T>
1959 static inline
1960 typename std::enable_if<
1961 std::is_same<T, GfHalf>::value ||
1962 std::is_same<T, float>::value ||
1963 std::is_same<T, double>::value>::type
_ReadPossiblyCompressedArray(Reader reader,ValueRep rep,VtArray<T> * out,CrateFile::Version ver,int)1964 _ReadPossiblyCompressedArray(
1965 Reader reader, ValueRep rep, VtArray<T> *out, CrateFile::Version ver, int)
1966 {
1967 // Version 0.6.0 introduced compressed floating point arrays.
1968 if (ver < CrateFile::Version(0,6,0) || !rep.IsCompressed()) {
1969 _ReadUncompressedArray(reader, rep, out, ver);
1970 return;
1971 }
1972
1973 out->resize(ver < CrateFile::Version(0,7,0) ?
1974 reader.template Read<uint32_t>() :
1975 reader.template Read<uint64_t>());
1976 auto odata = out->data();
1977 auto osize = out->size();
1978
1979 if (osize < MinCompressedArraySize) {
1980 // Not stored compressed.
1981 reader.ReadContiguous(odata, osize);
1982 return;
1983 }
1984
1985 // Read the code
1986 char code = reader.template Read<int8_t>();
1987 if (code == 'i') {
1988 // Compressed integers.
1989 vector<int32_t> ints(osize);
1990 _ReadCompressedInts(reader, ints.data(), ints.size());
1991 std::copy(ints.begin(), ints.end(), odata);
1992 } else if (code == 't') {
1993 // Lookup table & indexes.
1994 auto lutSize = reader.template Read<uint32_t>();
1995 vector<T> lut(lutSize);
1996 reader.ReadContiguous(lut.data(), lut.size());
1997 vector<uint32_t> indexes(osize);
1998 _ReadCompressedInts(reader, indexes.data(), indexes.size());
1999 auto o = odata;
2000 for (auto index: indexes) {
2001 *o++ = lut[index];
2002 }
2003 } else {
2004 // This is a corrupt data stream.
2005 TF_RUNTIME_ERROR("Corrupt data stream detected reading compressed "
2006 "array in <%s>", reader.crate->GetAssetPath().c_str());
2007 }
2008 }
2009
2010 // Array handler for types that support arrays -- does deduplication.
2011 template <class T>
2012 struct CrateFile::_ArrayValueHandlerBase<
2013 T, typename std::enable_if<ValueTypeTraits<T>::supportsArray>::type>
2014 : _ScalarValueHandlerBase<T>
2015 {
PackArrayUsd_CrateFile::CrateFile::_ArrayValueHandlerBase2016 ValueRep PackArray(_Writer w, VtArray<T> const &array) {
2017 auto result = ValueRepForArray<T>(0);
2018
2019 // If this is an empty array we inline it.
2020 if (array.empty())
2021 return result;
2022
2023 if (!_arrayDedup) {
2024 _arrayDedup.reset(
2025 new typename decltype(_arrayDedup)::element_type);
2026 }
2027
2028 auto iresult = _arrayDedup->emplace(array, result);
2029 ValueRep &target = iresult.first->second;
2030 if (iresult.second) {
2031 // Not yet present.
2032 if (w.crate->_packCtx->writeVersion < Version(0,5,0)) {
2033 target.SetPayload(w.Align(sizeof(uint64_t)));
2034 w.WriteAs<uint32_t>(1);
2035 w.WriteAs<uint32_t>(array.size());
2036 w.WriteContiguous(array.cdata(), array.size());
2037 } else {
2038 // If we're writing 0.5.0 or greater, see if we can possibly
2039 // compress this array.
2040 target = _WritePossiblyCompressedArray(
2041 w, array, w.crate->_packCtx->writeVersion, 0);
2042 }
2043 }
2044 return target;
2045 }
2046
2047 template <class Reader>
UnpackArrayUsd_CrateFile::CrateFile::_ArrayValueHandlerBase2048 void UnpackArray(Reader reader, ValueRep rep, VtArray<T> *out) const {
2049 // If payload is 0, it's an empty array.
2050 if (rep.GetPayload() == 0) {
2051 *out = VtArray<T>();
2052 return;
2053 }
2054 reader.Seek(rep.GetPayload());
2055
2056 // Check version
2057 Version fileVer(reader.crate->_boot);
2058 if (fileVer < Version(0,5,0)) {
2059 // Read and discard shape size.
2060 reader.template Read<uint32_t>();
2061 }
2062 _ReadPossiblyCompressedArray(reader, rep, out, fileVer, 0);
2063 }
2064
PackVtValueUsd_CrateFile::CrateFile::_ArrayValueHandlerBase2065 ValueRep PackVtValue(_Writer w, VtValue const &v) {
2066 return v.IsArrayValued() ?
2067 this->PackArray(w, v.UncheckedGet<VtArray<T>>()) :
2068 this->Pack(w, v.UncheckedGet<T>());
2069 }
2070
2071 template <class Reader>
UnpackVtValueUsd_CrateFile::CrateFile::_ArrayValueHandlerBase2072 void UnpackVtValue(Reader r, ValueRep rep, VtValue *out) {
2073 if (rep.IsArray()) {
2074 VtArray<T> array;
2075 this->UnpackArray(r, rep, &array);
2076 out->Swap(array);
2077 } else {
2078 T obj;
2079 this->Unpack(r, rep, &obj);
2080 out->Swap(obj);
2081 }
2082 }
2083
ClearUsd_CrateFile::CrateFile::_ArrayValueHandlerBase2084 void Clear() {
2085 // Invoke base implementation to clear scalar table.
2086 _ScalarValueHandlerBase<T>::Clear();
2087 _arrayDedup.reset();
2088 }
2089
2090 std::unique_ptr<
2091 std::unordered_map<VtArray<T>, ValueRep, _Hasher>> _arrayDedup;
2092 };
2093
2094 // _ValueHandler derives _ArrayValueHandlerBase, which in turn derives
2095 // _ScalarValueHandlerBase. Those templates are specialized to handle types
2096 // that support or do not support arrays and types that are inlined or not
2097 // inlined.
2098 template <class T>
2099 struct CrateFile::_ValueHandler : public _ArrayValueHandlerBase<T> {};
2100
2101
2102 ////////////////////////////////////////////////////////////////////////
2103 // CrateFile
2104
2105 /*static*/
2106 bool
CanRead(string const & assetPath)2107 CrateFile::CanRead(string const &assetPath)
2108 {
2109 // Fetch the asset from Ar.
2110 auto asset = ArGetResolver().OpenAsset(ArResolvedPath(assetPath));
2111 return asset && CanRead(assetPath, asset);
2112 }
2113
2114 /*static*/
2115 bool
CanRead(string const & assetPath,ArAssetSharedPtr const & asset)2116 CrateFile::CanRead(string const &assetPath, ArAssetSharedPtr const &asset)
2117 {
2118 // If the asset has a file, mark it random access to avoid prefetch.
2119 FILE *file; size_t offset;
2120 std::tie(file, offset) = asset->GetFileUnsafe();
2121 if (file) {
2122 ArchFileAdvise(file, offset, asset->GetSize(),
2123 ArchFileAdviceRandomAccess);
2124 }
2125
2126 TfErrorMark m;
2127 _ReadBootStrap(_AssetStream(asset), asset->GetSize());
2128
2129 // Clear any issued errors again to avoid propagation, and return true if
2130 // there were no errors issued.
2131 bool canRead = !m.Clear();
2132
2133 // Restore prefetching behavior to "normal".
2134 if (file) {
2135 ArchFileAdvise(file, offset, asset->GetSize(), ArchFileAdviceNormal);
2136 }
2137
2138 return canRead;
2139 }
2140
2141 /* static */
2142 std::unique_ptr<CrateFile>
CreateNew()2143 CrateFile::CreateNew()
2144 {
2145 const bool useMmap =
2146 !TfGetEnvSetting(USDC_USE_ASSET) &&
2147 !TfGetenvBool("USDC_USE_PREAD", false);
2148 return std::unique_ptr<CrateFile>(new CrateFile(useMmap));
2149 }
2150
2151 /* static */
2152 CrateFile::_FileMappingIPtr
_MmapAsset(char const * assetPath,ArAssetSharedPtr const & asset)2153 CrateFile::_MmapAsset(char const *assetPath, ArAssetSharedPtr const &asset)
2154 {
2155 FILE *file; size_t offset;
2156 std::tie(file, offset) = asset->GetFileUnsafe();
2157 std::string errMsg;
2158 auto mapping = _FileMappingIPtr(
2159 new _FileMapping(ArchMapFileReadWrite(file, &errMsg),
2160 offset, asset->GetSize()));
2161 if (!mapping->GetMapStart()) {
2162 TF_RUNTIME_ERROR("Couldn't map asset '%s'%s%s", assetPath,
2163 !errMsg.empty() ? ": " : "",
2164 errMsg.c_str());
2165 mapping.reset();
2166 }
2167 return mapping;
2168 }
2169
2170 /* static */
2171 CrateFile::_FileMappingIPtr
_MmapFile(char const * fileName,FILE * file)2172 CrateFile::_MmapFile(char const *fileName, FILE *file)
2173 {
2174 std::string errMsg;
2175 auto mapping = _FileMappingIPtr(
2176 new _FileMapping(ArchMapFileReadWrite(file, &errMsg)));
2177 if (!mapping->GetMapStart()) {
2178 TF_RUNTIME_ERROR("Couldn't map file '%s'%s%s", fileName,
2179 !errMsg.empty() ? ": " : "",
2180 errMsg.c_str());
2181 mapping.reset();
2182 }
2183 return mapping;
2184 }
2185
2186 /* static */
2187 std::unique_ptr<CrateFile>
Open(string const & assetPath)2188 CrateFile::Open(string const &assetPath)
2189 {
2190 TfAutoMallocTag tag2("Usd_CrateFile::CrateFile::Open");
2191 return Open(
2192 assetPath, ArGetResolver().OpenAsset(ArResolvedPath(assetPath)));
2193 }
2194
2195 std::unique_ptr<CrateFile>
Open(string const & assetPath,ArAssetSharedPtr const & asset)2196 CrateFile::Open(string const &assetPath, ArAssetSharedPtr const &asset)
2197 {
2198 TfAutoMallocTag tag2("Usd_CrateFile::CrateFile::Open");
2199
2200 std::unique_ptr<CrateFile> result;
2201
2202 if (!asset) {
2203 TF_RUNTIME_ERROR("Failed to open asset '%s'", assetPath.c_str());
2204 return result;
2205 }
2206
2207 if (!TfGetEnvSetting(USDC_USE_ASSET)) {
2208 // See if we can get an underlying FILE * for the asset.
2209 FILE *file; size_t offset;
2210 std::tie(file, offset) = asset->GetFileUnsafe();
2211 if (file) {
2212 // If so, then we'll either mmap it or use pread() on it.
2213 if (!TfGetenvBool("USDC_USE_PREAD", false)) {
2214 // Try to memory-map the file.
2215 auto mapping = _MmapAsset(assetPath.c_str(), asset);
2216 result.reset(new CrateFile(assetPath, ArchGetFileName(file),
2217 std::move(mapping), asset));
2218 } else {
2219 // Use pread with the asset's file.
2220 result.reset(new CrateFile(
2221 assetPath, ArchGetFileName(file),
2222 _FileRange(
2223 file, offset, asset->GetSize(),
2224 /*hasOwnership=*/ false),
2225 asset));
2226 }
2227 }
2228 }
2229
2230 if (!result) {
2231 // With no underlying FILE *, we'll go through ArAsset::Read() directly.
2232 result.reset(new CrateFile(assetPath, asset));
2233 }
2234
2235 // If the resulting CrateFile has no asset path, reading failed.
2236 if (result->GetAssetPath().empty())
2237 result.reset();
2238
2239 return result;
2240 }
2241
2242 /* static */
2243 TfToken const &
GetSoftwareVersionToken()2244 CrateFile::GetSoftwareVersionToken()
2245 {
2246 static TfToken tok(_SoftwareVersion.AsString());
2247 return tok;
2248 }
2249
2250 TfToken
GetFileVersionToken() const2251 CrateFile::GetFileVersionToken() const
2252 {
2253 return TfToken(Version(_boot).AsString());
2254 }
2255
CrateFile(bool useMmap)2256 CrateFile::CrateFile(bool useMmap)
2257 : _useMmap(useMmap)
2258 {
2259 _DoAllTypeRegistrations();
2260 }
2261
CrateFile(string const & assetPath,string const & fileName,_FileMappingIPtr mapping,ArAssetSharedPtr const & asset)2262 CrateFile::CrateFile(string const &assetPath, string const &fileName,
2263 _FileMappingIPtr mapping, ArAssetSharedPtr const &asset)
2264 : _mmapSrc(std::move(mapping))
2265 , _assetPath(assetPath)
2266 , _fileReadFrom(fileName)
2267 , _useMmap(true)
2268 {
2269 // Note that we intentionally do not store the asset -- we want to close the
2270 // file handle if possible.
2271 _DoAllTypeRegistrations();
2272 _InitMMap();
2273 }
2274
2275 void
_InitMMap()2276 CrateFile::_InitMMap() {
2277 if (_mmapSrc) {
2278 int64_t mapSize = _mmapSrc->GetLength();
2279
2280 // Mark the whole file as random access to start to avoid large NFS
2281 // prefetch. We explicitly prefetch the structural sections later.
2282 ArchMemAdvise(
2283 _mmapSrc->GetMapStart(), mapSize, ArchMemAdviceRandomAccess);
2284
2285 // If we're debugging access, allocate a debug page map.
2286 static string debugPageMapPattern = TfGetenv("USDC_DUMP_PAGE_MAPS");
2287 // If it's just '1' or '*' do everything, otherwise match.
2288 if (!debugPageMapPattern.empty() &&
2289 (debugPageMapPattern == "*" || debugPageMapPattern == "1" ||
2290 ArchRegex(debugPageMapPattern,
2291 ArchRegex::GLOB).Match(_assetPath))) {
2292 auto pageAlignedMapSize =
2293 (_mmapSrc->GetMapStart() + mapSize) -
2294 RoundToPageAddr(_mmapSrc->GetMapStart());
2295 int64_t npages =
2296 (pageAlignedMapSize + CRATE_PAGESIZE-1) / CRATE_PAGESIZE;
2297 _debugPageMap.reset(new char[npages]);
2298 memset(_debugPageMap.get(), 0, npages);
2299 }
2300
2301 // Make an mmap stream but disable auto prefetching -- the
2302 // _ReadStructuralSections() call manages prefetching itself using
2303 // higher-level knowledge.
2304 auto reader =
2305 _MakeReader(
2306 _MakeMmapStream(
2307 _mmapSrc.get(), _debugPageMap.get()).DisablePrefetch());
2308 TfErrorMark m;
2309 _ReadStructuralSections(reader, mapSize);
2310 if (!m.IsClean())
2311 _assetPath.clear();
2312
2313 // Restore default prefetch behavior if we're not doing custom prefetch.
2314 if (!_GetMMapPrefetchKB()) {
2315 ArchMemAdvise(
2316 _mmapSrc->GetMapStart(), mapSize, ArchMemAdviceNormal);
2317 }
2318 } else {
2319 _assetPath.clear();
2320 _fileReadFrom.clear();
2321 }
2322 }
2323
CrateFile(string const & assetPath,string const & fileName,_FileRange && inputFile,ArAssetSharedPtr const & asset)2324 CrateFile::CrateFile(string const &assetPath, string const &fileName,
2325 _FileRange &&inputFile, ArAssetSharedPtr const &asset)
2326 : _preadSrc(std::move(inputFile))
2327 , _assetSrc(asset)
2328 , _assetPath(assetPath)
2329 , _fileReadFrom(fileName)
2330 , _useMmap(false)
2331 {
2332 // Note that we *do* store the asset here, since we need to keep the FILE*
2333 // alive to pread from it.
2334 _DoAllTypeRegistrations();
2335 _InitPread();
2336 }
2337
2338 void
_InitPread()2339 CrateFile::_InitPread()
2340 {
2341 // Mark the whole file range as random access to start to avoid large NFS
2342 // prefetch. We explicitly prefetch the structural sections later.
2343 int64_t rangeLength = _preadSrc.GetLength();
2344 ArchFileAdvise(_preadSrc.file, _preadSrc.startOffset,
2345 rangeLength, ArchFileAdviceRandomAccess);
2346 auto reader = _MakeReader(_PreadStream(_preadSrc));
2347 TfErrorMark m;
2348 _ReadStructuralSections(reader, rangeLength);
2349 if (!m.IsClean()) {
2350 _assetPath.clear();
2351 _fileReadFrom.clear();
2352 }
2353 // Restore default prefetch behavior.
2354 ArchFileAdvise(_preadSrc.file, _preadSrc.startOffset,
2355 rangeLength, ArchFileAdviceNormal);
2356 }
2357
CrateFile(string const & assetPath,ArAssetSharedPtr const & asset)2358 CrateFile::CrateFile(string const &assetPath, ArAssetSharedPtr const &asset)
2359 : _assetSrc(asset)
2360 , _assetPath(assetPath)
2361 , _useMmap(false)
2362 {
2363 _DoAllTypeRegistrations();
2364 _InitAsset();
2365 }
2366
2367 void
_InitAsset()2368 CrateFile::_InitAsset()
2369 {
2370 auto reader = _MakeReader(_AssetStream(_assetSrc));
2371 TfErrorMark m;
2372 _ReadStructuralSections(reader, _assetSrc->GetSize());
2373 if (!m.IsClean())
2374 _assetPath.clear();
2375 }
2376
~CrateFile()2377 CrateFile::~CrateFile()
2378 {
2379 static std::mutex outputMutex;
2380
2381 // Dump a debug page map if requested.
2382 if (_useMmap && _mmapSrc && _debugPageMap) {
2383 auto mapStart = _mmapSrc->GetMapStart();
2384 int64_t startPage = GetPageNumber(mapStart);
2385 int64_t endPage = GetPageNumber(mapStart + _mmapSrc->GetLength() - 1);
2386 int64_t npages = 1 + endPage - startPage;
2387 std::unique_ptr<unsigned char []> mincoreMap(new unsigned char[npages]);
2388 void const *p = static_cast<void const *>(RoundToPageAddr(mapStart));
2389 if (!ArchQueryMappedMemoryResidency(
2390 p, npages*CRATE_PAGESIZE, mincoreMap.get())) {
2391 TF_WARN("failed to obtain memory residency information");
2392 return;
2393 }
2394 // Count the pages in core & accessed.
2395 int64_t pagesInCore = 0;
2396 int64_t pagesAccessed = 0;
2397 for (int64_t i = 0; i != npages; ++i) {
2398 bool inCore = mincoreMap[i] & 1;
2399 bool accessed = _debugPageMap[i] & 1;
2400 pagesInCore += (int)inCore;
2401 pagesAccessed += (int)accessed;
2402 if (accessed && inCore) {
2403 mincoreMap.get()[i] = '+';
2404 } else if (accessed) {
2405 mincoreMap.get()[i] = '!';
2406 } else if (inCore) {
2407 mincoreMap.get()[i] = '-';
2408 } else {
2409 mincoreMap.get()[i] = ' ';
2410 }
2411 }
2412
2413 std::lock_guard<std::mutex> lock(outputMutex);
2414
2415 printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
2416 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
2417 "page map for %s\n"
2418 "%" PRId64 " pages, %" PRId64 " used (%.1f%%), %" PRId64
2419 " in mem (%.1f%%)\n"
2420 "used %.1f%% of pages in mem\n"
2421 "legend: '+': in mem & used, '-': in mem & unused\n"
2422 " '!': not in mem & used, ' ': not in mem & unused\n"
2423 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
2424 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n",
2425 _assetPath.c_str(),
2426 npages,
2427 pagesAccessed, 100.0*pagesAccessed/(double)npages,
2428 pagesInCore, 100.0*pagesInCore/(double)npages,
2429 100.0*pagesAccessed / (double)pagesInCore);
2430
2431 constexpr int wrapCol = 80;
2432 int col = 0;
2433 for (int64_t i = 0; i != npages; ++i, ++col) {
2434 putchar(mincoreMap.get()[i]);
2435 if (col == wrapCol) {
2436 putchar('\n');
2437 col = -1;
2438 }
2439 }
2440 printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2441 "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
2442 }
2443
2444 // If we have zero copy ranges to detach, do it now.
2445 if (_useMmap && _mmapSrc) {
2446 _mmapSrc.reset();
2447 }
2448
2449 _DeleteValueHandlers();
2450 }
2451
2452 bool
CanPackTo(string const & fileName) const2453 CrateFile::CanPackTo(string const &fileName) const
2454 {
2455 if (_assetPath.empty()) {
2456 return true;
2457 }
2458 // Try to open \p fileName and get its filename.
2459 bool result = false;
2460 if (FILE *f = ArchOpenFile(fileName.c_str(), "rb")) {
2461 if (ArchGetFileName(f) == _fileReadFrom) {
2462 result = true;
2463 }
2464 fclose(f);
2465 }
2466 return result;
2467 }
2468
2469 CrateFile::Packer
StartPacking(string const & fileName)2470 CrateFile::StartPacking(string const &fileName)
2471 {
2472 #if AR_VERSION == 1
2473 // We open the file using the TfSafeOutputFile helper so that we can avoid
2474 // stomping on the file for other processes currently observing it, in the
2475 // case that we're replacing it. In the case where we're actually updating
2476 // an existing file, we have no choice but to modify it in place.
2477 TfErrorMark m;
2478 auto out = _assetPath.empty() ?
2479 TfSafeOutputFile::Replace(fileName) :
2480 TfSafeOutputFile::Update(fileName);
2481 if (!m.IsClean()) {
2482 // Error will be emitted when TfErrorMark goes out of scope
2483 }
2484 #else
2485 auto out = ArGetResolver().OpenAssetForWrite(
2486 ArResolvedPath(fileName),
2487 _assetPath.empty() ?
2488 ArResolver::WriteMode::Replace :
2489 ArResolver::WriteMode::Update);
2490 if (!out) {
2491 TF_RUNTIME_ERROR("Unable to open %s for write", fileName.c_str());
2492 }
2493 #endif
2494 else {
2495 // Create a packing context so we can start writing.
2496 _packCtx.reset(new _PackingContext(this, std::move(out), fileName));
2497 // Get rid of our local list of specs, if we have one -- the client is
2498 // required to repopulate it.
2499 vector<Spec>().swap(_specs);
2500 // If we have no tokens yet, insert a special token that cannot be used
2501 // as a prim property path element so that it gets token index 0.
2502 // There's a bug (github issue 811) in the compressed path code where it
2503 // uses negative indexes to indicate prim property path elements. That
2504 // fails for index 0. So inserting a token here that cannot be used as
2505 // a property path element sidesteps this.
2506 if (_tokens.empty()) {
2507 _AddToken(TfToken(";-)"));
2508 }
2509 }
2510 return Packer(this);
2511 }
2512
operator bool() const2513 CrateFile::Packer::operator bool() const {
2514 return _crate && _crate->_packCtx;
2515 }
2516
2517 #if AR_VERSION == 1
2518 bool
Close()2519 CrateFile::Packer::Close()
2520 {
2521 if (!TF_VERIFY(_crate && _crate->_packCtx))
2522 return false;
2523
2524 // Write contents.
2525 bool writeResult = _crate->_Write();
2526
2527 // If we wrote successfully, store the fileName and size.
2528 if (writeResult) {
2529 _crate->_assetPath = _crate->_packCtx->fileName;
2530 }
2531
2532 // Pull out the file handle and kill the packing context.
2533 TfSafeOutputFile outFile = _crate->_packCtx->ExtractOutputFile();
2534 _crate->_packCtx.reset();
2535
2536 if (!writeResult)
2537 return false;
2538
2539 // Note that once Save()d, we never go back to reading from an _assetSrc.
2540 _crate->_assetSrc.reset();
2541
2542 // Try to reuse the open FILE * if we can, otherwise open for read.
2543 _FileRange fileRange;
2544 if (outFile.IsOpenForUpdate()) {
2545 fileRange = _FileRange(outFile.ReleaseUpdatedFile(),
2546 /*startOffset=*/0, /*length=*/-1,
2547 /*hasOwnership=*/true);
2548 }
2549 else {
2550 outFile.Close();
2551 fileRange = _FileRange(ArchOpenFile(_crate->_assetPath.c_str(), "rb"),
2552 /*startOffset=*/0, /*length=*/-1,
2553 /*hasOwnership=*/true);
2554 }
2555
2556 // Reset the filename we've read content from.
2557 _crate->_fileReadFrom = ArchGetFileName(fileRange.file);
2558
2559 // Reset the mapping or file so we can read values from the newly
2560 // written file.
2561 if (_crate->_useMmap) {
2562 // Must remap the file.
2563 _crate->_mmapSrc =
2564 _MmapFile(_crate->_assetPath.c_str(), fileRange.file);
2565 if (!_crate->_mmapSrc)
2566 return false;
2567 _crate->_InitMMap();
2568 } else {
2569 // Must adopt the file handle if we don't already have one.
2570 _crate->_preadSrc = std::move(fileRange);
2571 _crate->_InitPread();
2572 }
2573
2574 return true;
2575 }
2576 #else
2577 bool
Close()2578 CrateFile::Packer::Close()
2579 {
2580 if (!TF_VERIFY(_crate && _crate->_packCtx))
2581 return false;
2582
2583 // Write contents. Always close the output asset even if writing failed.
2584 bool writeResult = _crate->_Write();
2585 writeResult &= _crate->_packCtx->CloseOutputAsset();
2586
2587 // If we wrote successfully, store the fileName.
2588 if (writeResult) {
2589 _crate->_assetPath = _crate->_packCtx->fileName;
2590 }
2591
2592 _crate->_packCtx.reset();
2593
2594 if (!writeResult)
2595 return false;
2596
2597 // Reset so we can read values from the newly written asset.
2598 // See CrateFile::Open.
2599 auto asset = ArGetResolver().OpenAsset(ArResolvedPath(_crate->_assetPath));
2600 if (!asset) {
2601 return false;
2602 }
2603
2604 if (!TfGetEnvSetting(USDC_USE_ASSET)) {
2605 FILE *file; size_t offset;
2606 std::tie(file, offset) = asset->GetFileUnsafe();
2607 if (file) {
2608 // Reset the filename we've read content from.
2609 _crate->_fileReadFrom = ArchGetFileName(file);
2610
2611 if (_crate->_useMmap) {
2612 // Must remap the file.
2613 _crate->_mmapSrc = _MmapFile(_crate->_assetPath.c_str(), file);
2614 if (!_crate->_mmapSrc) {
2615 return false;
2616 }
2617 _crate->_assetSrc.reset();
2618 _crate->_InitMMap();
2619 }
2620 else {
2621 _crate->_preadSrc = _FileRange(
2622 file, offset, asset->GetSize(), /*hasOwnership=*/false);
2623 _crate->_assetSrc = asset;
2624 _crate->_InitPread();
2625 }
2626
2627 return true;
2628 }
2629 }
2630
2631 _crate->_mmapSrc.reset();
2632 _crate->_preadSrc = _FileRange();
2633 _crate->_assetSrc = asset;
2634 _crate->_InitAsset();
2635
2636 return true;
2637 }
2638 #endif
2639
Packer(Packer && other)2640 CrateFile::Packer::Packer(Packer &&other) : _crate(other._crate)
2641 {
2642 other._crate = nullptr;
2643 }
2644
2645 CrateFile::Packer &
operator =(Packer && other)2646 CrateFile::Packer::operator=(Packer &&other)
2647 {
2648 _crate = other._crate;
2649 other._crate = nullptr;
2650 return *this;
2651 }
2652
~Packer()2653 CrateFile::Packer::~Packer()
2654 {
2655 if (_crate)
2656 _crate->_packCtx.reset();
2657 }
2658
2659 vector<tuple<string, int64_t, int64_t>>
GetSectionsNameStartSize() const2660 CrateFile::GetSectionsNameStartSize() const
2661 {
2662 vector<tuple<string, int64_t, int64_t> > result;
2663 for (auto const &sec: _toc.sections) {
2664 result.emplace_back(sec.name, sec.start, sec.size);
2665 }
2666 return result;
2667 }
2668
2669 template <class Fn>
2670 void
_WriteSection(_Writer & w,_SectionName name,_TableOfContents & toc,Fn writeFn) const2671 CrateFile::_WriteSection(
2672 _Writer &w, _SectionName name, _TableOfContents &toc, Fn writeFn) const
2673 {
2674 toc.sections.emplace_back(name.c_str(), w.Tell(), 0);
2675 writeFn();
2676 toc.sections.back().size = w.Tell() - toc.sections.back().start;
2677 }
2678
2679 void
_AddDeferredSpecs()2680 CrateFile::_AddDeferredSpecs()
2681 {
2682 // A map from sample time to VtValues within TimeSamples instances in
2683 // _deferredSpecs.
2684 boost::container::flat_map<double, vector<VtValue *>> allValuesAtAllTimes;
2685
2686 // Search for the TimeSamples, add to the allValuesAtAllTimes.
2687 for (auto &spec: _deferredSpecs) {
2688 for (auto &tsf: spec.timeSampleFields) {
2689 for (size_t i = 0; i != tsf.second.values.size(); ++i) {
2690 if (!tsf.second.values[i].IsHolding<ValueRep>()) {
2691 allValuesAtAllTimes[tsf.second.times.Get()[i]].push_back(
2692 &tsf.second.values[i]);
2693 }
2694 }
2695 }
2696 }
2697
2698 // Now walk through allValuesAtAllTimes in order and pack all the values,
2699 // swapping them out with the resulting reps. This ensures that when we
2700 // pack the specs, which will re-pack the values, they'll be noops since
2701 // they are just holding value reps that point into the file.
2702 for (auto const &p: allValuesAtAllTimes) {
2703 for (VtValue *val: p.second)
2704 *val = _PackValue(*val);
2705 }
2706
2707 // Now we've transformed all the VtValues in all the timeSampleFields to
2708 // ValueReps. We can call _AddField and add them (and any of the other
2709 // deferred fields) to ordinaryFields, then add the spec.
2710 for (auto &spec: _deferredSpecs) {
2711 // Add the deferred ordinary fields
2712 for (auto &fv: spec.deferredOrdinaryFields) {
2713 spec.ordinaryFields.push_back(_AddField(fv));
2714 }
2715 // Add the deferred time sample fields
2716 for (auto &p: spec.timeSampleFields) {
2717 spec.ordinaryFields.push_back(
2718 _AddField(make_pair(p.first, VtValue::Take(p.second))));
2719 }
2720 _specs.emplace_back(spec.path, spec.specType,
2721 _AddFieldSet(spec.ordinaryFields));
2722 }
2723
2724 TfReset(_deferredSpecs);
2725 }
2726
2727 bool
_Write()2728 CrateFile::_Write()
2729 {
2730 // First, add any _deferredSpecs, including packing time sample field values
2731 // time-by-time to ensure that all the data for given times is collocated.
2732 _AddDeferredSpecs();
2733
2734 // Now proceed with writing.
2735 _Writer w(this);
2736
2737 _TableOfContents toc;
2738
2739 // Write out the sections we don't know about that the packing context
2740 // captured.
2741 using std::get;
2742 for (auto const &s: _packCtx->unknownSections) {
2743 _Section sec(get<0>(s).c_str(), w.Tell(), get<2>(s));
2744 w.WriteContiguous(get<1>(s).get(), sec.size);
2745 toc.sections.push_back(sec);
2746 }
2747
2748 _WriteSection(w, _TokensSectionName, toc, [this, &w]() {_WriteTokens(w);});
2749 _WriteSection(
2750 w, _StringsSectionName, toc, [this, &w]() {w.Write(_strings);});
2751 _WriteSection(w, _FieldsSectionName, toc, [this, &w]() {_WriteFields(w);});
2752 _WriteSection(
2753 w, _FieldSetsSectionName, toc, [this, &w]() {_WriteFieldSets(w);});
2754 _WriteSection(w, _PathsSectionName, toc, [this, &w]() {_WritePaths(w);});
2755 _WriteSection(w, _SpecsSectionName, toc, [this, &w]() {_WriteSpecs(w);});
2756
2757 _BootStrap boot(_packCtx->writeVersion);
2758
2759 // Record TOC location, and write it.
2760 boot.tocOffset = w.Tell();
2761 w.Write(toc);
2762
2763 // Write bootstrap at start of file.
2764 w.Seek(0);
2765 w.Write(boot);
2766
2767 // Flush any buffered writes.
2768 w.Flush();
2769
2770 _toc = toc;
2771 _boot = boot;
2772
2773 // Clear dedup tables.
2774 _ClearValueHandlerDedupTables();
2775
2776 return true;
2777 }
2778
2779 void
_AddSpec(const SdfPath & path,SdfSpecType type,const std::vector<FieldValuePair> & fields)2780 CrateFile::_AddSpec(const SdfPath &path, SdfSpecType type,
2781 const std::vector<FieldValuePair> &fields)
2782 {
2783 vector<FieldIndex> ordinaryFields; // non time-sample valued fields.
2784 vector<pair<TfToken, TimeSamples>> timeSampleFields;
2785 vector<FieldValuePair> versionUpgradePendingFields;
2786
2787 auto _IsCompatiblePre08PayloadValue = [this](const VtValue &v)
2788 {
2789 // There are two cases where a field's value is backwards compatible
2790 // with verion 0.7.0.
2791 // 1. The value holds an SdfPayload with an identity layer offset.
2792 // 2. The value holds a ValueRep that packs a payload read from a 0.7.0
2793 // or earlier crate file.
2794 // In both cases, the value will need to be repacked if the version
2795 // needs to be upgraded to 0.8.0 or higher for any reason.
2796 return (v.IsHolding<SdfPayload>() &&
2797 v.UncheckedGet<SdfPayload>().GetLayerOffset().IsIdentity()) ||
2798 (Version(this->_boot) < Version(0, 8, 0) &&
2799 v.IsHolding<ValueRep>() &&
2800 v.UncheckedGet<ValueRep>().GetType() == TypeEnum::Payload);
2801 };
2802
2803 ordinaryFields.reserve(fields.size());
2804 for (auto const &p: fields) {
2805 if (p.second.IsHolding<TimeSamples>() &&
2806 p.second.UncheckedGet<TimeSamples>().IsInMemory()) {
2807 // If any of the fields here are TimeSamples, then defer adding
2808 // this spec to the call to _Write(). In _Write(), we'll add all
2809 // the sample values time-by-time to ensure that all the data for a
2810 // given sample time is as collocated as possible in the file.
2811 timeSampleFields.emplace_back(
2812 p.first, p.second.UncheckedGet<TimeSamples>());
2813 } else if (_packCtx->writeVersion < Version(0, 8, 0) &&
2814 _IsCompatiblePre08PayloadValue(p.second)) {
2815 // If the file we're writing has not yet been upgraded to a 0.8.0 or
2816 // later version and the field value is a SdfPayload that can still
2817 // be represented in a older version, then we defer this spec until
2818 // the call to _Write. This is to make sure that if we end up
2819 // needing to upgrade the file version for some other field or spec,
2820 // that we still write all SdfPayloads in the file using the current
2821 // format instead of having a mix of formats depending on the order
2822 // we wrote our payload values in.
2823 versionUpgradePendingFields.push_back(p);
2824 } else {
2825 ordinaryFields.push_back(_AddField(p));
2826 }
2827 }
2828
2829 // If we have no time sample fields or version upgrade pending fields, we
2830 // can just add the spec now. Otherwise defer this spec until _Write().
2831 if (timeSampleFields.empty() && versionUpgradePendingFields.empty()) {
2832 _specs.emplace_back(_AddPath(path), type, _AddFieldSet(ordinaryFields));
2833 }
2834 else {
2835 _deferredSpecs.emplace_back(
2836 _AddPath(path), type,
2837 std::move(ordinaryFields),
2838 std::move(versionUpgradePendingFields),
2839 std::move(timeSampleFields));
2840 }
2841 }
2842
2843 VtValue
_GetTimeSampleValueImpl(TimeSamples const & ts,size_t i) const2844 CrateFile::_GetTimeSampleValueImpl(TimeSamples const &ts, size_t i) const
2845 {
2846 // Need to read the rep from the file for index i.
2847 auto offset = ts.valuesFileOffset + i * sizeof(ValueRep);
2848 if (_useMmap) {
2849 auto reader = _MakeReader(
2850 _MakeMmapStream(_mmapSrc.get(), _debugPageMap.get()));
2851 reader.Seek(offset);
2852 return VtValue(reader.Read<ValueRep>());
2853 } else if (_preadSrc) {
2854 auto reader = _MakeReader(_PreadStream(_preadSrc));
2855 reader.Seek(offset);
2856 return VtValue(reader.Read<ValueRep>());
2857 } else {
2858 auto reader = _MakeReader(_AssetStream(_assetSrc));
2859 reader.Seek(offset);
2860 return VtValue(reader.Read<ValueRep>());
2861 }
2862 }
2863
2864 void
_MakeTimeSampleValuesMutableImpl(TimeSamples & ts) const2865 CrateFile::_MakeTimeSampleValuesMutableImpl(TimeSamples &ts) const
2866 {
2867 // Read out the reps into the vector.
2868 ts.values.resize(ts.times.Get().size());
2869 if (_useMmap) {
2870 auto reader = _MakeReader(
2871 _MakeMmapStream(_mmapSrc.get(), _debugPageMap.get()));
2872 reader.Seek(ts.valuesFileOffset);
2873 for (size_t i = 0, n = ts.times.Get().size(); i != n; ++i)
2874 ts.values[i] = reader.Read<ValueRep>();
2875 } else if (_preadSrc) {
2876 auto reader = _MakeReader(_PreadStream(_preadSrc));
2877 reader.Seek(ts.valuesFileOffset);
2878 for (size_t i = 0, n = ts.times.Get().size(); i != n; ++i)
2879 ts.values[i] = reader.Read<ValueRep>();
2880 } else {
2881 auto reader = _MakeReader(_AssetStream(_assetSrc));
2882 reader.Seek(ts.valuesFileOffset);
2883 for (size_t i = 0, n = ts.times.Get().size(); i != n; ++i)
2884 ts.values[i] = reader.Read<ValueRep>();
2885 }
2886 // Now in memory, no longer reading everything from file.
2887 ts.valueRep = ValueRep(0);
2888 }
2889
2890 void
_WriteFields(_Writer & w)2891 CrateFile::_WriteFields(_Writer &w)
2892 {
2893 if (_packCtx->writeVersion < Version(0,4,0)) {
2894 // Old-style uncompressed fields.
2895 w.Write(_fields);
2896 } else {
2897 // Compressed fields in 0.4.0.
2898
2899 // Total # of fields.
2900 w.WriteAs<uint64_t>(_fields.size());
2901
2902 // Token index values.
2903 vector<uint32_t> tokenIndexVals(_fields.size());
2904 std::transform(_fields.begin(), _fields.end(),
2905 tokenIndexVals.begin(),
2906 [](Field const &f) { return f.tokenIndex.value; });
2907 std::unique_ptr<char[]> compBuffer(
2908 new char[Usd_IntegerCompression::
2909 GetCompressedBufferSize(tokenIndexVals.size())]);
2910
2911 size_t tokenIndexesSize = Usd_IntegerCompression::CompressToBuffer(
2912 tokenIndexVals.data(), tokenIndexVals.size(), compBuffer.get());
2913 w.WriteAs<uint64_t>(tokenIndexesSize);
2914 w.WriteContiguous(compBuffer.get(), tokenIndexesSize);
2915
2916 // ValueReps.
2917 vector<uint64_t> reps(_fields.size());
2918 std::transform(_fields.begin(), _fields.end(),
2919 reps.begin(),
2920 [](Field const &f) { return f.valueRep.data; });
2921
2922 std::unique_ptr<char[]> compBuffer2(
2923 new char[TfFastCompression::
2924 GetCompressedBufferSize(reps.size() * sizeof(reps[0]))]);
2925 uint64_t repsSize = TfFastCompression::CompressToBuffer(
2926 reinterpret_cast<char *>(reps.data()),
2927 compBuffer2.get(), reps.size() * sizeof(reps[0]));
2928 w.WriteAs<uint64_t>(repsSize);
2929 w.WriteContiguous(compBuffer2.get(), repsSize);
2930 }
2931 }
2932
2933 void
_WriteFieldSets(_Writer & w)2934 CrateFile::_WriteFieldSets(_Writer &w)
2935 {
2936 if (_packCtx->writeVersion < Version(0,4,0)) {
2937 // Old-style uncompressed fieldSets.
2938 w.Write(_fieldSets);
2939 } else {
2940 // Compressed fieldSets.
2941 vector<uint32_t> fieldSetsVals(_fieldSets.size());
2942 std::transform(_fieldSets.begin(), _fieldSets.end(),
2943 fieldSetsVals.begin(),
2944 [](FieldIndex fi) { return fi.value; });
2945 std::unique_ptr<char[]> compBuffer(
2946 new char[Usd_IntegerCompression::
2947 GetCompressedBufferSize(fieldSetsVals.size())]);
2948 // Total # of fieldSetVals.
2949 w.WriteAs<uint64_t>(fieldSetsVals.size());
2950
2951 size_t fsetsSize = Usd_IntegerCompression::CompressToBuffer(
2952 fieldSetsVals.data(), fieldSetsVals.size(), compBuffer.get());
2953 w.WriteAs<uint64_t>(fsetsSize);
2954 w.WriteContiguous(compBuffer.get(), fsetsSize);
2955 }
2956 }
2957
2958 void
_WritePaths(_Writer & w)2959 CrateFile::_WritePaths(_Writer &w)
2960 {
2961 // Write the total # of paths.
2962 w.WriteAs<uint64_t>(_paths.size());
2963
2964 if (_packCtx->writeVersion < Version(0,4,0)) {
2965 // Old-style uncompressed paths.
2966 SdfPathTable<PathIndex> pathToIndexTable;
2967 for (auto const &item: _packCtx->pathToPathIndex)
2968 pathToIndexTable[item.first] = item.second;
2969 _WritePathTree(w, pathToIndexTable.begin(), pathToIndexTable.end());
2970 WorkSwapDestroyAsync(pathToIndexTable);
2971 } else {
2972 // Write compressed paths.
2973 vector<pair<SdfPath, PathIndex>> ppaths;
2974 ppaths.reserve(_paths.size());
2975 for (auto const &p: _paths) {
2976 if (!p.IsEmpty()) {
2977 ppaths.emplace_back(p, _packCtx->pathToPathIndex[p]);
2978 }
2979 }
2980 std::sort(ppaths.begin(), ppaths.end(),
2981 [](pair<SdfPath, PathIndex> const &l,
2982 pair<SdfPath, PathIndex> const &r) {
2983 return l.first < r.first;
2984 });
2985 _WriteCompressedPathData(w, ppaths);
2986 }
2987 }
2988
2989 void
_WriteSpecs(_Writer & w)2990 CrateFile::_WriteSpecs(_Writer &w)
2991 {
2992 // VERSIONING: If we're writing version 0.0.1, we need to convert to the old
2993 // form.
2994 if (_packCtx->writeVersion == Version(0,0,1)) {
2995 // Copy and write old-structure specs.
2996 vector<Spec_0_0_1> old(_specs.begin(), _specs.end());
2997 w.Write(old);
2998 } else if (_packCtx->writeVersion < Version(0,4,0)) {
2999 w.Write(_specs);
3000 } else {
3001 // Version 0.4.0 introduces compressed specs. We write three lists of
3002 // integers here, pathIndexes, fieldSetIndexes, specTypes.
3003 std::unique_ptr<char[]> compBuffer(
3004 new char[Usd_IntegerCompression::
3005 GetCompressedBufferSize(_specs.size())]);
3006 vector<uint32_t> tmp(_specs.size());
3007
3008 // Total # of specs.
3009 w.WriteAs<uint64_t>(_specs.size());
3010
3011 // pathIndexes.
3012 std::transform(_specs.begin(), _specs.end(), tmp.begin(),
3013 [](Spec const &s) { return s.pathIndex.value; });
3014 size_t pathIndexesSize = Usd_IntegerCompression::CompressToBuffer(
3015 tmp.data(), tmp.size(), compBuffer.get());
3016 w.WriteAs<uint64_t>(pathIndexesSize);
3017 w.WriteContiguous(compBuffer.get(), pathIndexesSize);
3018
3019 // fieldSetIndexes.
3020 std::transform(_specs.begin(), _specs.end(), tmp.begin(),
3021 [](Spec const &s) { return s.fieldSetIndex.value; });
3022 size_t fsetIndexesSize = Usd_IntegerCompression::CompressToBuffer(
3023 tmp.data(), tmp.size(), compBuffer.get());
3024 w.WriteAs<uint64_t>(fsetIndexesSize);
3025 w.WriteContiguous(compBuffer.get(), fsetIndexesSize);
3026
3027 // specTypes.
3028 std::transform(_specs.begin(), _specs.end(), tmp.begin(),
3029 [](Spec const &s) { return s.specType; });
3030 size_t specTypesSize = Usd_IntegerCompression::CompressToBuffer(
3031 tmp.data(), tmp.size(), compBuffer.get());
3032 w.WriteAs<uint64_t>(specTypesSize);
3033 w.WriteContiguous(compBuffer.get(), specTypesSize);
3034 }
3035 }
3036
3037 template <class Iter>
3038 Iter
_WritePathTree(_Writer & w,Iter cur,Iter end)3039 CrateFile::_WritePathTree(_Writer &w, Iter cur, Iter end)
3040 {
3041 // Each element looks like this:
3042 //
3043 // (pathIndex, pathElementTokenIndex, hasChild, hasSibling)
3044 // [offset to sibling, if hasSibling and hasChild]
3045 //
3046 // If the element's hasChild bit is set, then the very next element is its
3047 // first child. If the element's hasChild bit is not set and its hasSibling
3048 // bit is set, then the very next element is its next sibling. If both bits
3049 // are set then an offset to the sibling appears in the stream and the
3050 // following element is the first child.
3051 //
3052
3053 for (Iter next = cur; cur != end; cur = next) {
3054 Iter nextSubtree = cur.GetNextSubtree();
3055 ++next;
3056
3057 bool hasChild = next != nextSubtree &&
3058 next->first.GetParentPath() == cur->first;
3059
3060 bool hasSibling = nextSubtree != end &&
3061 nextSubtree->first.GetParentPath() == cur->first.GetParentPath();
3062
3063 bool isPrimPropertyPath = cur->first.IsPrimPropertyPath();
3064
3065 auto elementToken = isPrimPropertyPath ?
3066 cur->first.GetNameToken() : cur->first.GetElementToken();
3067
3068 // VERSIONING: If we're writing version 0.0.1, make sure we use the
3069 // right header type.
3070 if (_packCtx->writeVersion == Version(0,0,1)) {
3071 _PathItemHeader_0_0_1 header(
3072 cur->second, _GetIndexForToken(elementToken),
3073 static_cast<uint8_t>(
3074 (hasChild ? _PathItemHeader::HasChildBit : 0) |
3075 (hasSibling ? _PathItemHeader::HasSiblingBit : 0) |
3076 (isPrimPropertyPath ?
3077 _PathItemHeader::IsPrimPropertyPathBit : 0)));
3078 w.Write(header);
3079 } else {
3080 _PathItemHeader header(
3081 cur->second, _GetIndexForToken(elementToken),
3082 static_cast<uint8_t>(
3083 (hasChild ? _PathItemHeader::HasChildBit : 0) |
3084 (hasSibling ? _PathItemHeader::HasSiblingBit : 0) |
3085 (isPrimPropertyPath ?
3086 _PathItemHeader::IsPrimPropertyPathBit : 0)));
3087 w.Write(header);
3088 }
3089
3090 // If there's both a child and a sibling, make space for the sibling
3091 // offset.
3092 int64_t siblingPtrOffset = -1;
3093 if (hasSibling && hasChild) {
3094 siblingPtrOffset = w.Tell();
3095 // Temporarily write a bogus value just to make space.
3096 w.WriteAs<int64_t>(-1);
3097 }
3098 // If there is a child, recurse.
3099 if (hasChild)
3100 next = _WritePathTree(w, next, end);
3101
3102 // If we have a sibling, then fill in the offset that it will be
3103 // written at (it will be written next).
3104 if (hasSibling && hasChild) {
3105 int64_t cur = w.Tell();
3106 w.Seek(siblingPtrOffset);
3107 w.Write(cur);
3108 w.Seek(cur);
3109 }
3110
3111 if (!hasSibling)
3112 return next;
3113 }
3114 return end;
3115 }
3116
3117 template <class Iter>
3118 Iter
_BuildCompressedPathDataRecursive(size_t & curIndex,Iter cur,Iter end,vector<uint32_t> & pathIndexes,vector<int32_t> & elementTokenIndexes,vector<int32_t> & jumps)3119 CrateFile::_BuildCompressedPathDataRecursive(
3120 size_t &curIndex, Iter cur, Iter end,
3121 vector<uint32_t> &pathIndexes,
3122 vector<int32_t> &elementTokenIndexes,
3123 vector<int32_t> &jumps)
3124 {
3125 auto getNextSubtree = [](Iter cur, Iter end) {
3126 Iter start = cur;
3127 while (cur != end && cur->first.HasPrefix(start->first)) {
3128 ++cur;
3129 }
3130 return cur;
3131 };
3132
3133 for (Iter next = cur; cur != end; cur = next) {
3134
3135 Iter nextSubtree = getNextSubtree(cur, end);
3136 ++next;
3137
3138 bool hasChild = next != nextSubtree &&
3139 next->first.GetParentPath() == cur->first;
3140
3141 bool hasSibling = nextSubtree != end &&
3142 nextSubtree->first.GetParentPath() == cur->first.GetParentPath();
3143
3144 bool isPrimPropertyPath = cur->first.IsPrimPropertyPath();
3145
3146 auto elementToken = isPrimPropertyPath ?
3147 cur->first.GetNameToken() : cur->first.GetElementToken();
3148
3149 size_t thisIndex = curIndex++;
3150 pathIndexes[thisIndex] = cur->second.value;
3151 elementTokenIndexes[thisIndex] = _GetIndexForToken(elementToken).value;
3152 if (isPrimPropertyPath) {
3153 elementTokenIndexes[thisIndex] = -elementTokenIndexes[thisIndex];
3154 }
3155
3156 // If there is a child, recurse.
3157 if (hasChild) {
3158 next = _BuildCompressedPathDataRecursive(
3159 curIndex, next, end, pathIndexes, elementTokenIndexes, jumps);
3160 }
3161
3162 // If we have a sibling, then fill in the offset that it will be
3163 // written at (it will be written next).
3164 if (hasSibling && hasChild) {
3165 jumps[thisIndex] = curIndex-thisIndex;
3166 } else if (hasSibling) {
3167 jumps[thisIndex] = 0;
3168 } else if (hasChild) {
3169 jumps[thisIndex] = -1;
3170 } else {
3171 jumps[thisIndex] = -2;
3172 }
3173
3174 if (!hasSibling)
3175 return next;
3176 }
3177 return end;
3178 }
3179
3180 template <class Container>
3181 void
_WriteCompressedPathData(_Writer & w,Container const & pathVec)3182 CrateFile::_WriteCompressedPathData(_Writer &w, Container const &pathVec)
3183 {
3184 // We build up three integer arrays representing the paths:
3185 // - pathIndexes[] :
3186 // the index in _paths corresponding to this item.
3187 // - elementTokenIndexes[] :
3188 // the element to append to the parent to get this path -- negative
3189 // elements are prim property path elements.
3190 // - jumps[] :
3191 // 0=only a sibling, -1=only a child, -2=leaf, else has both, positive
3192 // sibling index offset.
3193 //
3194 // This is vaguely similar to the _PathItemHeader struct used in prior
3195 // versions.
3196
3197 // Write the # of encoded paths. This can differ from the size of _paths
3198 // since we do not write out the empty path.
3199 w.WriteAs<uint64_t>(pathVec.size());
3200
3201 vector<uint32_t> pathIndexes;
3202 vector<int32_t> elementTokenIndexes;
3203 vector<int32_t> jumps;
3204
3205 pathIndexes.resize(pathVec.size());
3206 elementTokenIndexes.resize(pathVec.size());
3207 jumps.resize(pathVec.size());
3208
3209 size_t index = 0;
3210 _BuildCompressedPathDataRecursive(
3211 index, pathVec.begin(), pathVec.end(),
3212 pathIndexes, elementTokenIndexes, jumps);
3213
3214 // Compress and store the arrays.
3215 std::unique_ptr<char[]> compBuffer(
3216 new char[Usd_IntegerCompression::
3217 GetCompressedBufferSize(pathVec.size())]);
3218
3219 // pathIndexes.
3220 uint64_t pathIndexesSize = Usd_IntegerCompression::CompressToBuffer(
3221 pathIndexes.data(), pathIndexes.size(), compBuffer.get());
3222 w.WriteAs<uint64_t>(pathIndexesSize);
3223 w.WriteContiguous(compBuffer.get(), pathIndexesSize);
3224
3225 // elementTokenIndexes.
3226 uint64_t elemToksSize = Usd_IntegerCompression::CompressToBuffer(
3227 elementTokenIndexes.data(), elementTokenIndexes.size(),
3228 compBuffer.get());
3229 w.WriteAs<uint64_t>(elemToksSize);
3230 w.WriteContiguous(compBuffer.get(), elemToksSize);
3231
3232 // jumps.
3233 uint64_t jumpsSize = Usd_IntegerCompression::CompressToBuffer(
3234 jumps.data(), jumps.size(), compBuffer.get());
3235 w.WriteAs<uint64_t>(jumpsSize);
3236 w.WriteContiguous(compBuffer.get(), jumpsSize);
3237 }
3238
3239 void
_WriteTokens(_Writer & w)3240 CrateFile::_WriteTokens(_Writer &w) {
3241 // # of strings.
3242 w.WriteAs<uint64_t>(_tokens.size());
3243 if (_packCtx->writeVersion < Version(0,4,0)) {
3244 // Count total bytes.
3245 uint64_t totalBytes = 0;
3246 for (auto const &t: _tokens)
3247 totalBytes += t.GetString().size() + 1;
3248 w.WriteAs<uint64_t>(totalBytes);
3249 // Token data.
3250 for (auto const &t: _tokens) {
3251 auto const &str = t.GetString();
3252 w.WriteContiguous(str.c_str(), str.size() + 1);
3253 }
3254 } else {
3255 // Version 0.4.0 compresses tokens.
3256 vector<char> tokenData;
3257 for (auto const &t: _tokens) {
3258 auto const &str = t.GetString();
3259 char const *cstr = str.c_str();
3260 tokenData.insert(tokenData.end(), cstr, cstr + str.size() + 1);
3261 }
3262 w.WriteAs<uint64_t>(tokenData.size());
3263 std::unique_ptr<char[]> compressed(
3264 new char[TfFastCompression::GetCompressedBufferSize(tokenData.size())]);
3265 uint64_t compressedSize = TfFastCompression::CompressToBuffer(
3266 tokenData.data(), compressed.get(), tokenData.size());
3267 w.WriteAs<uint64_t>(compressedSize);
3268 w.WriteContiguous(compressed.get(), compressedSize);
3269 }
3270 }
3271
3272 template <class Reader>
3273 void
_ReadStructuralSections(Reader reader,int64_t fileSize)3274 CrateFile::_ReadStructuralSections(Reader reader, int64_t fileSize)
3275 {
3276 TfErrorMark m;
3277 _boot = _ReadBootStrap(reader.src, fileSize);
3278 if (m.IsClean()) _toc = _ReadTOC(reader, _boot);
3279 if (m.IsClean()) _PrefetchStructuralSections(reader);
3280 if (m.IsClean()) _ReadTokens(reader);
3281 if (m.IsClean()) _ReadStrings(reader);
3282 if (m.IsClean()) _ReadFields(reader);
3283 if (m.IsClean()) _ReadFieldSets(reader);
3284 if (m.IsClean()) _ReadPaths(reader);
3285 if (m.IsClean()) _ReadSpecs(reader);
3286 }
3287
3288 template <class ByteStream>
3289 /*static*/
3290 CrateFile::_BootStrap
_ReadBootStrap(ByteStream src,int64_t fileSize)3291 CrateFile::_ReadBootStrap(ByteStream src, int64_t fileSize)
3292 {
3293 _BootStrap b;
3294 if (fileSize < (int64_t)sizeof(_BootStrap)) {
3295 TF_RUNTIME_ERROR("File too small to contain bootstrap structure");
3296 return b;
3297 }
3298 src.Seek(0);
3299 src.Read(&b, sizeof(b));
3300 // Sanity check.
3301 if (memcmp(b.ident, USDC_IDENT, sizeof(b.ident))) {
3302 TF_RUNTIME_ERROR("Usd crate bootstrap section corrupt");
3303 }
3304 // Check version.
3305 else if (!_SoftwareVersion.CanRead(Version(b))) {
3306 TF_RUNTIME_ERROR(
3307 "Usd crate file version mismatch -- file is %s, "
3308 "software supports %s", Version(b).AsString().c_str(),
3309 _SoftwareVersion.AsString().c_str());
3310 }
3311 // Check that the table of contents is not past the end of the file. This
3312 // catches some cases where a file was corrupted by truncation.
3313 else if (fileSize <= b.tocOffset) {
3314 TF_RUNTIME_ERROR(
3315 "Usd crate file corrupt, possibly truncated: table of contents "
3316 "at offset %" PRId64 " but file size is %" PRId64,
3317 b.tocOffset, fileSize);
3318 }
3319 return b;
3320 }
3321
3322 template <class Reader>
3323 void
_PrefetchStructuralSections(Reader reader) const3324 CrateFile::_PrefetchStructuralSections(Reader reader) const
3325 {
3326 // Go through the _toc and find its maximal range, then ask the reader to
3327 // prefetch that range.
3328 int64_t min = -1, max = -1;
3329 for (_Section const &sec: _toc.sections) {
3330 if (min == -1 || (sec.start < min))
3331 min = sec.start;
3332 int64_t end = sec.start + sec.size;
3333 if (max == -1 || (end > max))
3334 max = end;
3335 }
3336 if (min != -1 && max != -1)
3337 reader.Prefetch(min, max-min);
3338 }
3339
3340 template <class Reader>
3341 CrateFile::_TableOfContents
_ReadTOC(Reader reader,_BootStrap const & b) const3342 CrateFile::_ReadTOC(Reader reader, _BootStrap const &b) const
3343 {
3344 reader.Seek(b.tocOffset);
3345 return reader.template Read<_TableOfContents>();
3346 }
3347
3348 template <class Reader>
3349 void
_ReadFieldSets(Reader reader)3350 CrateFile::_ReadFieldSets(Reader reader)
3351 {
3352 TfAutoMallocTag tag("_ReadFieldSets");
3353 if (auto fieldSetsSection = _toc.GetSection(_FieldSetsSectionName)) {
3354 reader.Seek(fieldSetsSection->start);
3355
3356 if (Version(_boot) < Version(0,4,0)) {
3357 _fieldSets = reader.template Read<decltype(_fieldSets)>();
3358 } else {
3359 // Compressed fieldSets in 0.4.0.
3360 auto numFieldSets = reader.template Read<uint64_t>();
3361 _fieldSets.resize(numFieldSets);
3362 vector<uint32_t> tmp(numFieldSets);
3363 _ReadCompressedInts(reader, tmp.data(), numFieldSets);
3364 for (size_t i = 0; i != numFieldSets; ++i) {
3365 _fieldSets[i].value = tmp[i];
3366 }
3367 }
3368
3369 // FieldSets must be terminated by a default-constructed FieldIndex.
3370 if (!_fieldSets.empty() && _fieldSets.back() != FieldIndex()) {
3371 TF_RUNTIME_ERROR("Corrupt field sets in crate file");
3372 _fieldSets.back() = FieldIndex();
3373 }
3374 }
3375 }
3376
3377 template <class Reader>
3378 void
_ReadFields(Reader reader)3379 CrateFile::_ReadFields(Reader reader)
3380 {
3381 TfAutoMallocTag tag("_ReadFields");
3382 if (auto fieldsSection = _toc.GetSection(_FieldsSectionName)) {
3383 reader.Seek(fieldsSection->start);
3384 if (Version(_boot) < Version(0,4,0)) {
3385 _fields = reader.template Read<decltype(_fields)>();
3386 } else {
3387 // Compressed fields in 0.4.0.
3388 auto numFields = reader.template Read<uint64_t>();
3389 _fields.resize(numFields);
3390 vector<uint32_t> tmp(numFields);
3391 _ReadCompressedInts(reader, tmp.data(), tmp.size());
3392 for (size_t i = 0; i != numFields; ++i) {
3393 _fields[i].tokenIndex.value = tmp[i];
3394 }
3395
3396 // Compressed value reps.
3397 uint64_t repsSize = reader.template Read<uint64_t>();
3398 std::unique_ptr<char[]> compBuffer(new char[repsSize]);
3399 reader.ReadContiguous(compBuffer.get(), repsSize);
3400 vector<uint64_t> repsData;
3401 repsData.resize(numFields);
3402 TfFastCompression::DecompressFromBuffer(
3403 compBuffer.get(), reinterpret_cast<char *>(repsData.data()),
3404 repsSize, repsData.size() * sizeof(repsData[0]));
3405
3406 for (size_t i = 0; i != numFields; ++i) {
3407 _fields[i].valueRep.data = repsData[i];
3408 }
3409 }
3410 }
3411 }
3412
3413 template <class Reader>
3414 void
_ReadSpecs(Reader reader)3415 CrateFile::_ReadSpecs(Reader reader)
3416 {
3417 TfAutoMallocTag tag("_ReadSpecs");
3418 if (auto specsSection = _toc.GetSection(_SpecsSectionName)) {
3419 reader.Seek(specsSection->start);
3420 // VERSIONING: Have to read either old or new style specs.
3421 if (Version(_boot) == Version(0,0,1)) {
3422 vector<Spec_0_0_1> old = reader.template Read<decltype(old)>();
3423 _specs.resize(old.size());
3424 copy(old.begin(), old.end(), _specs.begin());
3425 } else if (Version(_boot) < Version(0,4,0)) {
3426 _specs = reader.template Read<decltype(_specs)>();
3427 } else {
3428 // Version 0.4.0 specs are compressed
3429 auto numSpecs = reader.template Read<uint64_t>();
3430 _specs.resize(numSpecs);
3431
3432 // Create temporary space for decompressing.
3433 _CompressedIntsReader cr;
3434 vector<uint32_t> tmp(numSpecs);
3435
3436 // pathIndexes.
3437 cr.Read(reader, tmp.data(), numSpecs);
3438 for (size_t i = 0; i != numSpecs; ++i) {
3439 _specs[i].pathIndex.value = tmp[i];
3440 }
3441
3442 // fieldSetIndexes.
3443 cr.Read(reader, tmp.data(), numSpecs);
3444 for (size_t i = 0; i != numSpecs; ++i) {
3445 _specs[i].fieldSetIndex.value = tmp[i];
3446 }
3447
3448 // specTypes.
3449 cr.Read(reader, tmp.data(), numSpecs);
3450 for (size_t i = 0; i != numSpecs; ++i) {
3451 _specs[i].specType = static_cast<SdfSpecType>(tmp[i]);
3452 }
3453 }
3454 }
3455 }
3456
3457 template <class Reader>
3458 void
_ReadStrings(Reader reader)3459 CrateFile::_ReadStrings(Reader reader)
3460 {
3461 TfAutoMallocTag tag("_ReadStrings");
3462 if (auto stringsSection = _toc.GetSection(_StringsSectionName)) {
3463 reader.Seek(stringsSection->start);
3464 _strings = reader.template Read<decltype(_strings)>();
3465 }
3466 }
3467
3468 template <class Reader>
3469 void
_ReadTokens(Reader reader)3470 CrateFile::_ReadTokens(Reader reader)
3471 {
3472 TfAutoMallocTag tag("_ReadTokens");
3473
3474 auto tokensSection = _toc.GetSection(_TokensSectionName);
3475 if (!tokensSection)
3476 return;
3477
3478 reader.Seek(tokensSection->start);
3479
3480 // Read number of tokens.
3481 auto numTokens = reader.template Read<uint64_t>();
3482
3483 RawDataPtr chars;
3484 char const *charsEnd = nullptr;
3485
3486 Version fileVer(_boot);
3487 if (fileVer < Version(0,4,0)) {
3488 // XXX: To support pread(), we need to read the whole thing into memory
3489 // to make tokens out of it. This is a pessimization vs mmap, from
3490 // which we can just construct from the chars directly.
3491 auto tokensNumBytes = reader.template Read<uint64_t>();
3492 chars.reset(new char[tokensNumBytes]);
3493 charsEnd = chars.get() + tokensNumBytes;
3494 reader.ReadContiguous(chars.get(), tokensNumBytes);
3495 } else {
3496 // Compressed token data.
3497 uint64_t uncompressedSize = reader.template Read<uint64_t>();
3498 uint64_t compressedSize = reader.template Read<uint64_t>();
3499 chars.reset(new char[uncompressedSize]);
3500 charsEnd = chars.get() + uncompressedSize;
3501 RawDataPtr compressed(new char[compressedSize]);
3502 reader.ReadContiguous(compressed.get(), compressedSize);
3503 TfFastCompression::DecompressFromBuffer(
3504 compressed.get(), chars.get(), compressedSize, uncompressedSize);
3505 }
3506
3507 // Check/ensure that we're null terminated.
3508 if (chars.get() != charsEnd && charsEnd[-1] != '\0') {
3509 TF_RUNTIME_ERROR("Tokens section not null-terminated in crate file");
3510 const_cast<char *>(charsEnd)[-1] = '\0';
3511 }
3512
3513 // Now we read that many null-terminated strings into _tokens.
3514 char const *p = chars.get();
3515 _tokens.clear();
3516 _tokens.resize(numTokens);
3517
3518 WorkWithScopedParallelism([this, &p, charsEnd, numTokens]() {
3519 WorkDispatcher wd;
3520 struct MakeToken {
3521 void operator()() const { (*tokens)[index] = TfToken(str); }
3522 vector<TfToken> *tokens;
3523 size_t index;
3524 char const *str;
3525 };
3526 size_t i = 0;
3527 for (; p < charsEnd && i != numTokens; ++i) {
3528 MakeToken mt { &_tokens, i, p };
3529 wd.Run(mt);
3530 p += strlen(p) + 1;
3531 }
3532 wd.Wait();
3533 if (i != numTokens) {
3534 TF_RUNTIME_ERROR("Crate file claims %zu tokens, found %zu",
3535 numTokens, i);
3536 }
3537 });
3538
3539 WorkSwapDestroyAsync(chars);
3540 }
3541
3542 template <class Reader>
3543 void
_ReadPaths(Reader reader)3544 CrateFile::_ReadPaths(Reader reader)
3545 {
3546 TfAutoMallocTag tag("_ReadPaths");
3547
3548 auto pathsSection = _toc.GetSection(_PathsSectionName);
3549 if (!pathsSection)
3550 return;
3551
3552 reader.Seek(pathsSection->start);
3553
3554 // Read # of paths, and fill the _paths vector with empty paths.
3555 _paths.resize(reader.template Read<uint64_t>());
3556 std::fill(_paths.begin(), _paths.end(), SdfPath());
3557
3558 WorkWithScopedParallelism([this, &reader]() {
3559 WorkDispatcher dispatcher;
3560 // VERSIONING: PathItemHeader changes size from 0.0.1 to 0.1.0.
3561 Version fileVer(_boot);
3562 if (fileVer == Version(0,0,1)) {
3563 _ReadPathsImpl<_PathItemHeader_0_0_1>(reader, dispatcher);
3564 } else if (fileVer < Version(0,4,0)) {
3565 _ReadPathsImpl<_PathItemHeader>(reader, dispatcher);
3566 } else {
3567 // 0.4.0 has compressed paths.
3568 _ReadCompressedPaths(reader, dispatcher);
3569 }
3570 });
3571 }
3572
3573 template <class Header, class Reader>
3574 void
_ReadPathsImpl(Reader reader,WorkDispatcher & dispatcher,SdfPath parentPath)3575 CrateFile::_ReadPathsImpl(Reader reader,
3576 WorkDispatcher &dispatcher,
3577 SdfPath parentPath)
3578 {
3579 bool hasChild = false, hasSibling = false;
3580 do {
3581 auto h = reader.template Read<Header>();
3582 if (parentPath.IsEmpty()) {
3583 parentPath = SdfPath::AbsoluteRootPath();
3584 _paths[h.index.value] = parentPath;
3585 } else {
3586 auto const &elemToken = _tokens[h.elementTokenIndex.value];
3587 _paths[h.index.value] =
3588 h.bits & _PathItemHeader::IsPrimPropertyPathBit ?
3589 parentPath.AppendProperty(elemToken) :
3590 parentPath.AppendElementToken(elemToken);
3591 }
3592
3593 // If we have either a child or a sibling but not both, then just
3594 // continue to the neighbor. If we have both then spawn a task for the
3595 // sibling and do the child ourself. We think that our path trees tend
3596 // to be broader more often than deep.
3597
3598 hasChild = h.bits & _PathItemHeader::HasChildBit;
3599 hasSibling = h.bits & _PathItemHeader::HasSiblingBit;
3600
3601 if (hasChild) {
3602 if (hasSibling) {
3603 // Branch off a parallel task for the sibling subtree.
3604 auto siblingOffset = reader.template Read<int64_t>();
3605 dispatcher.Run(
3606 [this, reader,
3607 siblingOffset, &dispatcher, parentPath]() mutable {
3608 // XXX Remove these tags when bug #132031 is addressed
3609 TfAutoMallocTag2 tag("Usd", "Usd_CrateDataImpl::Open");
3610 TfAutoMallocTag2 tag2("Usd_CrateFile::CrateFile::Open",
3611 "_ReadPaths");
3612 reader.Seek(siblingOffset);
3613 _ReadPathsImpl<Header>(reader, dispatcher, parentPath);
3614 });
3615 }
3616 // Have a child (may have also had a sibling). Reset parent path.
3617 parentPath = _paths[h.index.value];
3618 }
3619 // If we had only a sibling, we just continue since the parent path is
3620 // unchanged and the next thing in the reader stream is the sibling's
3621 // header.
3622 } while (hasChild || hasSibling);
3623 }
3624
3625 template <class Reader>
3626 void
_ReadCompressedPaths(Reader reader,WorkDispatcher & dispatcher)3627 CrateFile::_ReadCompressedPaths(Reader reader,
3628 WorkDispatcher &dispatcher)
3629 {
3630 // Read compressed data first.
3631 vector<uint32_t> pathIndexes;
3632 vector<int32_t> elementTokenIndexes;
3633 vector<int32_t> jumps;
3634
3635 // Read number of encoded paths.
3636 size_t numPaths = reader.template Read<uint64_t>();
3637
3638 _CompressedIntsReader cr;
3639
3640 // pathIndexes.
3641 pathIndexes.resize(numPaths);
3642 cr.Read(reader, pathIndexes.data(), numPaths);
3643
3644 #ifdef PXR_PREFER_SAFETY_OVER_SPEED
3645 // Range check the pathIndexes, which index into _paths.
3646 for (const uint32_t pathIndex: pathIndexes) {
3647 if (pathIndex >= _paths.size()) {
3648 TF_RUNTIME_ERROR("Corrupt path index in crate file (%u >= %zu)",
3649 pathIndex, _paths.size());
3650 return;
3651 }
3652 }
3653 #endif // PXR_PREFER_SAFETY_OVER_SPEED
3654
3655 // elementTokenIndexes.
3656 elementTokenIndexes.resize(numPaths);
3657 cr.Read(reader, elementTokenIndexes.data(), numPaths);
3658
3659 #ifdef PXR_PREFER_SAFETY_OVER_SPEED
3660 // Range check the pathIndexes, which index (by absolute value) into _tokens.
3661 for (const int32_t elementTokenIndex: elementTokenIndexes) {
3662 if (static_cast<size_t>(
3663 std::abs(elementTokenIndex)) >= _tokens.size()) {
3664 TF_RUNTIME_ERROR("Corrupt path element token index in crate file "
3665 "(%d >= %zu)",
3666 std::abs(elementTokenIndex), _tokens.size());
3667 return;
3668 }
3669 }
3670 #endif // PXR_PREFER_SAFETY_OVER_SPEED
3671
3672 // jumps.
3673 jumps.resize(numPaths);
3674 cr.Read(reader, jumps.data(), numPaths);
3675
3676 // Now build the paths.
3677 _BuildDecompressedPathsImpl(pathIndexes, elementTokenIndexes, jumps, 0,
3678 SdfPath(), dispatcher);
3679
3680 dispatcher.Wait();
3681 }
3682
3683 void
_BuildDecompressedPathsImpl(vector<uint32_t> const & pathIndexes,vector<int32_t> const & elementTokenIndexes,vector<int32_t> const & jumps,size_t curIndex,SdfPath parentPath,WorkDispatcher & dispatcher)3684 CrateFile::_BuildDecompressedPathsImpl(
3685 vector<uint32_t> const &pathIndexes,
3686 vector<int32_t> const &elementTokenIndexes,
3687 vector<int32_t> const &jumps,
3688 size_t curIndex,
3689 SdfPath parentPath,
3690 WorkDispatcher &dispatcher)
3691 {
3692 bool hasChild = false, hasSibling = false;
3693 do {
3694 auto thisIndex = curIndex++;
3695 if (parentPath.IsEmpty()) {
3696 parentPath = SdfPath::AbsoluteRootPath();
3697 _paths[pathIndexes[thisIndex]] = parentPath;
3698 } else {
3699 int32_t tokenIndex = elementTokenIndexes[thisIndex];
3700 bool isPrimPropertyPath = tokenIndex < 0;
3701 tokenIndex = std::abs(tokenIndex);
3702 auto const &elemToken = _tokens[tokenIndex];
3703 _paths[pathIndexes[thisIndex]] =
3704 isPrimPropertyPath ?
3705 parentPath.AppendProperty(elemToken) :
3706 parentPath.AppendElementToken(elemToken);
3707 }
3708
3709 // If we have either a child or a sibling but not both, then just
3710 // continue to the neighbor. If we have both then spawn a task for the
3711 // sibling and do the child ourself. We think that our path trees tend
3712 // to be broader more often than deep.
3713
3714 hasChild = (jumps[thisIndex] > 0) || (jumps[thisIndex] == -1);
3715 hasSibling = (jumps[thisIndex] >= 0);
3716
3717 if (hasChild) {
3718 if (hasSibling) {
3719 // Branch off a parallel task for the sibling subtree.
3720 auto siblingIndex = thisIndex + jumps[thisIndex];
3721 #ifdef PXR_PREFER_SAFETY_OVER_SPEED
3722 // Range check siblingIndex, which indexes into pathIndexes.
3723 if (siblingIndex >= pathIndexes.size()) {
3724 TF_RUNTIME_ERROR(
3725 "Corrupt paths jumps table in crate file (jump:%d + "
3726 "thisIndex:%zu >= %zu)",
3727 jumps[thisIndex], thisIndex, pathIndexes.size());
3728 return;
3729 }
3730 #endif
3731 dispatcher.Run(
3732 [this, &pathIndexes, &elementTokenIndexes, &jumps,
3733 siblingIndex, &dispatcher, parentPath]() mutable {
3734 // XXX Remove these tags when bug #132031 is addressed
3735 TfAutoMallocTag2 tag("Usd", "Usd_CrateDataImpl::Open");
3736 TfAutoMallocTag2 tag2("Usd_CrateFile::CrateFile::Open",
3737 "_ReadPaths");
3738 _BuildDecompressedPathsImpl(
3739 pathIndexes, elementTokenIndexes, jumps,
3740 siblingIndex, parentPath, dispatcher);
3741 });
3742 }
3743 // Have a child (may have also had a sibling). Reset parent path.
3744 parentPath = _paths[pathIndexes[thisIndex]];
3745 }
3746 // If we had only a sibling, we just continue since the parent path is
3747 // unchanged and the next thing in the reader stream is the sibling's
3748 // header.
3749 } while (hasChild || hasSibling);
3750 }
3751
3752 void
_ReadRawBytes(int64_t start,int64_t size,char * buf) const3753 CrateFile::_ReadRawBytes(int64_t start, int64_t size, char *buf) const
3754 {
3755 if (_useMmap) {
3756 auto reader = _MakeReader(
3757 _MakeMmapStream(_mmapSrc.get(), _debugPageMap.get()));
3758 reader.Seek(start);
3759 reader.template ReadContiguous<char>(buf, size);
3760 } else if (_preadSrc) {
3761 auto reader = _MakeReader(_PreadStream(_preadSrc));
3762 reader.Seek(start);
3763 reader.template ReadContiguous<char>(buf, size);
3764 } else {
3765 auto reader = _MakeReader(_AssetStream(_assetSrc));
3766 reader.Seek(start);
3767 reader.template ReadContiguous<char>(buf, size);
3768 }
3769 }
3770
3771 PathIndex
_AddPath(const SdfPath & path)3772 CrateFile::_AddPath(const SdfPath &path)
3773 {
3774 // Try to insert this path.
3775 auto iresult = _packCtx->pathToPathIndex.emplace(path, PathIndex());
3776 if (iresult.second) {
3777 // If this is a target path, add the target.
3778 if (path.IsTargetPath())
3779 _AddPath(path.GetTargetPath());
3780
3781 // Not present -- ensure parent is added.
3782 if (path != SdfPath::AbsoluteRootPath())
3783 _AddPath(path.GetParentPath());
3784
3785 // Add a token for this path's element string, unless it's a prim
3786 // property path, in which case we add the name. We treat prim property
3787 // paths separately since there are so many, and the name with the dot
3788 // just basically doubles the number of tokens we store.
3789 _AddToken(path.IsPrimPropertyPath() ? path.GetNameToken() :
3790 path.GetElementToken());
3791
3792 // Add to the vector and insert the index.
3793 iresult.first->second = PathIndex(_paths.size());
3794 _paths.emplace_back(path);
3795 }
3796 return iresult.first->second;
3797 }
3798
3799 FieldSetIndex
_AddFieldSet(const std::vector<FieldIndex> & fieldIndexes)3800 CrateFile::_AddFieldSet(const std::vector<FieldIndex> &fieldIndexes)
3801 {
3802 auto iresult =
3803 _packCtx->fieldsToFieldSetIndex.emplace(fieldIndexes, FieldSetIndex());
3804 if (iresult.second) {
3805 // Not yet present. Copy the fields to _fieldSets, terminate, and store
3806 // the start index.
3807 iresult.first->second = FieldSetIndex(_fieldSets.size());
3808 _fieldSets.insert(_fieldSets.end(),
3809 fieldIndexes.begin(), fieldIndexes.end());
3810 _fieldSets.push_back(FieldIndex());
3811 }
3812 return iresult.first->second;
3813 }
3814
3815 FieldIndex
_AddField(const FieldValuePair & fv)3816 CrateFile::_AddField(const FieldValuePair &fv)
3817 {
3818 Field field(_AddToken(fv.first), _PackValue(fv.second));
3819 auto iresult = _packCtx->fieldToFieldIndex.emplace(field, FieldIndex());
3820 if (iresult.second) {
3821 // Not yet present.
3822 iresult.first->second = FieldIndex(_fields.size());
3823 _fields.push_back(field);
3824 }
3825 return iresult.first->second;
3826 }
3827
3828 TokenIndex
_AddToken(const TfToken & token)3829 CrateFile::_AddToken(const TfToken &token)
3830 {
3831 auto iresult = _packCtx->tokenToTokenIndex.emplace(token, TokenIndex());
3832 if (iresult.second) {
3833 // Not yet present.
3834 iresult.first->second = TokenIndex(_tokens.size());
3835 _tokens.emplace_back(token);
3836 }
3837 return iresult.first->second;
3838 }
3839
3840 TokenIndex
_GetIndexForToken(const TfToken & token) const3841 CrateFile::_GetIndexForToken(const TfToken &token) const
3842 {
3843 auto iter = _packCtx->tokenToTokenIndex.find(token);
3844 if (!TF_VERIFY(iter != _packCtx->tokenToTokenIndex.end()))
3845 return TokenIndex();
3846 return iter->second;
3847 }
3848
3849 StringIndex
_AddString(const string & str)3850 CrateFile::_AddString(const string &str)
3851 {
3852 auto iresult = _packCtx->stringToStringIndex.emplace(str, StringIndex());
3853 if (iresult.second) {
3854 // Not yet present.
3855 iresult.first->second = StringIndex(_strings.size());
3856 _strings.push_back(_AddToken(TfToken(str)));
3857 }
3858 return iresult.first->second;
3859 }
3860
3861 template <class T>
3862 CrateFile::_ValueHandler<T> &
_GetValueHandler()3863 CrateFile::_GetValueHandler() {
3864 return *static_cast<_ValueHandler<T> *>(
3865 _valueHandlers[static_cast<int>(TypeEnumFor<T>())]);
3866 }
3867
3868 template <class T>
3869 CrateFile::_ValueHandler<T> const &
_GetValueHandler() const3870 CrateFile::_GetValueHandler() const {
3871 return *static_cast<_ValueHandler<T> const *>(
3872 _valueHandlers[static_cast<int>(TypeEnumFor<T>())]);
3873 }
3874
3875 template <class T>
3876 ValueRep
_PackValue(T const & v)3877 CrateFile::_PackValue(T const &v) {
3878 return _GetValueHandler<T>().Pack(_Writer(this), v);
3879 }
3880
3881 template <class T>
3882 ValueRep
_PackValue(VtArray<T> const & v)3883 CrateFile::_PackValue(VtArray<T> const &v) {
3884 return _GetValueHandler<T>().PackArray(_Writer(this), v);
3885 }
3886
3887 ValueRep
_PackValue(VtValue const & v)3888 CrateFile::_PackValue(VtValue const &v)
3889 {
3890 // If the value is holding a ValueRep, then we can just return it, we don't
3891 // need to add anything.
3892 if (v.IsHolding<ValueRep>()) {
3893 const ValueRep &valueRep = v.UncheckedGet<ValueRep>();
3894 // Special case for packed SdfPayloads. If the packed value is from
3895 // a pre 0.8.0 version but we're writing to a 0.8.0 or later file, we
3896 // need to unpack and repack the payload. Otherwise the packed payload
3897 // won't have a layer offset and will not be read correctly when reading
3898 // the new file.
3899 if (valueRep.GetType() == TypeEnum::Payload &&
3900 Version(_boot) < Version(0, 8, 0) &&
3901 _packCtx->writeVersion >= Version(0, 8, 0)) {
3902 VtValue payloadValue;
3903 _UnpackValue(valueRep, &payloadValue);
3904 return _PackValue(payloadValue);
3905 }
3906 return valueRep;
3907 }
3908
3909 // Similarly if the value is holding a TimeSamples that is still reading
3910 // from the file, we can return its held rep and continue.
3911 if (v.IsHolding<TimeSamples>()) {
3912 auto const &ts = v.UncheckedGet<TimeSamples>();
3913 if (!ts.IsInMemory())
3914 return ts.valueRep;
3915 }
3916
3917 std::type_index ti =
3918 v.IsArrayValued() ? v.GetElementTypeid() : v.GetTypeid();
3919
3920 auto it = _packValueFunctions.find(ti);
3921 if (it != _packValueFunctions.end())
3922 return it->second(v);
3923
3924 TF_CODING_ERROR("Attempted to pack unsupported type '%s' "
3925 "(%s)", ArchGetDemangled(ti).c_str(),
3926 TfStringify(v).c_str());
3927
3928 return ValueRep(0);
3929 }
3930
3931 template <class T>
3932 void
_UnpackValue(ValueRep rep,T * out) const3933 CrateFile::_UnpackValue(ValueRep rep, T *out) const
3934 {
3935 auto const &h = _GetValueHandler<T>();
3936 if (_useMmap) {
3937 h.Unpack(
3938 _MakeReader(
3939 _MakeMmapStream(_mmapSrc.get(), _debugPageMap.get())), rep, out);
3940 } else if (_preadSrc) {
3941 h.Unpack(_MakeReader(_PreadStream(_preadSrc)), rep, out);
3942 } else {
3943 h.Unpack(_MakeReader(_AssetStream(_assetSrc)), rep, out);
3944 }
3945 }
3946
3947 template <class T>
3948 void
_UnpackValue(ValueRep rep,VtArray<T> * out) const3949 CrateFile::_UnpackValue(ValueRep rep, VtArray<T> *out) const {
3950 auto const &h = _GetValueHandler<T>();
3951 if (_useMmap) {
3952 h.UnpackArray(
3953 _MakeReader(
3954 _MakeMmapStream(_mmapSrc.get(), _debugPageMap.get())), rep, out);
3955 } else if (_preadSrc) {
3956 h.UnpackArray(_MakeReader(_PreadStream(_preadSrc)), rep, out);
3957 } else {
3958 h.UnpackArray(_MakeReader(_AssetStream(_assetSrc)), rep, out);
3959 }
3960 }
3961
3962 void
_UnpackValue(ValueRep rep,VtValue * result) const3963 CrateFile::_UnpackValue(ValueRep rep, VtValue *result) const {
3964 // Look up the function for the type enum, and invoke it.
3965 auto repType = rep.GetType();
3966 if (repType == TypeEnum::Invalid || repType >= TypeEnum::NumTypes) {
3967 TF_CODING_ERROR("Attempted to unpack unsupported type enum value %d",
3968 static_cast<int>(repType));
3969 return;
3970 }
3971 auto index = static_cast<int>(repType);
3972 if (_useMmap) {
3973 _unpackValueFunctionsMmap[index](rep, result);
3974 } else if (_preadSrc) {
3975 _unpackValueFunctionsPread[index](rep, result);
3976 } else {
3977 _unpackValueFunctionsAsset[index](rep, result);
3978 }
3979 }
3980
3981 struct _GetTypeidForArrayTypes {
3982 template <class T>
GetUsd_CrateFile::_GetTypeidForArrayTypes3983 static std::type_info const &Get(bool array) {
3984 return array ? typeid(VtArray<T>) : typeid(T);
3985 }
3986 };
3987
3988 struct _GetTypeidForNonArrayTypes {
3989 template <class T>
GetUsd_CrateFile::_GetTypeidForNonArrayTypes3990 static std::type_info const &Get(bool) {
3991 return typeid(T);
3992 }
3993 };
3994
3995 template <bool SupportsArray>
3996 using _GetTypeid = typename std::conditional<SupportsArray,
3997 _GetTypeidForArrayTypes,
3998 _GetTypeidForNonArrayTypes>::type;
3999
4000 std::type_info const &
GetTypeid(ValueRep rep) const4001 CrateFile::GetTypeid(ValueRep rep) const
4002 {
4003 switch (rep.GetType()) {
4004 #define xx(ENUMNAME, _unused, T, SUPPORTSARRAY) \
4005 case TypeEnum::ENUMNAME: \
4006 return _GetTypeid<SUPPORTSARRAY>::Get<T>(rep.IsArray());
4007
4008 #include "crateDataTypes.h"
4009
4010 #undef xx
4011
4012 default:
4013 return typeid(void);
4014 };
4015 }
4016
4017 template <class T>
4018 void
_DoTypeRegistration()4019 CrateFile::_DoTypeRegistration() {
4020 auto typeEnumIndex = static_cast<int>(TypeEnumFor<T>());
4021 auto valueHandler = new _ValueHandler<T>();
4022 _valueHandlers[typeEnumIndex] = valueHandler;
4023
4024 // Value Pack/Unpack functions.
4025 _packValueFunctions[std::type_index(typeid(T))] =
4026 [this, valueHandler](VtValue const &val) {
4027 return valueHandler->PackVtValue(_Writer(this), val);
4028 };
4029
4030 _unpackValueFunctionsPread[typeEnumIndex] =
4031 [this, valueHandler](ValueRep rep, VtValue *out) {
4032 valueHandler->UnpackVtValue(
4033 _MakeReader(_PreadStream(_preadSrc)), rep, out);
4034 };
4035
4036 _unpackValueFunctionsMmap[typeEnumIndex] =
4037 [this, valueHandler](ValueRep rep, VtValue *out) {
4038 valueHandler->UnpackVtValue(
4039 _MakeReader(_MakeMmapStream(_mmapSrc.get(),
4040 _debugPageMap.get())), rep, out);
4041 };
4042
4043 _unpackValueFunctionsAsset[typeEnumIndex] =
4044 [this, valueHandler](ValueRep rep, VtValue *out) {
4045 valueHandler->UnpackVtValue(
4046 _MakeReader(_AssetStream(_assetSrc)), rep, out);
4047 };
4048 }
4049
4050 // Functions that populate the value read/write functions.
4051 void
_DoAllTypeRegistrations()4052 CrateFile::_DoAllTypeRegistrations() {
4053 TfAutoMallocTag tag("Usd_CrateFile::CrateFile::_DoAllTypeRegistrations");
4054 #define xx(_unused1, _unused2, CPPTYPE, _unused3) \
4055 _DoTypeRegistration<CPPTYPE>();
4056
4057 #include "crateDataTypes.h"
4058
4059 #undef xx
4060 }
4061
4062 void
_DeleteValueHandlers()4063 CrateFile::_DeleteValueHandlers() {
4064 #define xx(_unused1, _unused2, T, _unused3) \
4065 delete static_cast<_ValueHandler<T> *>( \
4066 _valueHandlers[static_cast<int>(TypeEnumFor<T>())]);
4067
4068 #include "crateDataTypes.h"
4069
4070 #undef xx
4071 }
4072
4073 void
_ClearValueHandlerDedupTables()4074 CrateFile::_ClearValueHandlerDedupTables() {
4075 #define xx(_unused1, _unused2, T, _unused3) \
4076 static_cast<_ValueHandler<T> *>( \
4077 _valueHandlers[static_cast<int>(TypeEnumFor<T>())])->Clear();
4078
4079 #include "crateDataTypes.h"
4080
4081 #undef xx
4082 }
4083
4084
4085 /* static */
4086 bool
_IsKnownSection(char const * name)4087 CrateFile::_IsKnownSection(char const *name) {
4088 for (auto const &secName: _KnownSections) {
4089 if (secName == name)
4090 return true;
4091 }
4092 return false;
4093 }
4094
4095 #ifdef PXR_PREFER_SAFETY_OVER_SPEED
4096 CrateFile::Field const &
_GetEmptyField() const4097 CrateFile::_GetEmptyField() const
4098 {
4099 static Field empty;
4100 return empty;
4101 }
4102
4103 std::string const &
_GetEmptyString() const4104 CrateFile::_GetEmptyString() const
4105 {
4106 static std::string empty;
4107 return empty;
4108 }
4109
4110 TfToken const &
_GetEmptyToken() const4111 CrateFile::_GetEmptyToken() const
4112 {
4113 static TfToken empty;
4114 return empty;
4115 }
4116 #endif // PXR_PREFER_SAFETY_OVER_SPEED
4117
Spec(Spec_0_0_1 const & s)4118 CrateFile::Spec::Spec(Spec_0_0_1 const &s)
4119 : Spec(s.pathIndex, s.specType, s.fieldSetIndex) {}
4120
Spec_0_0_1(Spec const & s)4121 CrateFile::Spec_0_0_1::Spec_0_0_1(Spec const &s)
4122 : Spec_0_0_1(s.pathIndex, s.specType, s.fieldSetIndex) {}
4123
_BootStrap()4124 CrateFile::_BootStrap::_BootStrap() : _BootStrap(_SoftwareVersion) {}
4125
_BootStrap(Version const & ver)4126 CrateFile::_BootStrap::_BootStrap(Version const &ver)
4127 {
4128 memset(this, 0, sizeof(*this));
4129 tocOffset = 0;
4130 memcpy(ident, USDC_IDENT, sizeof(ident));
4131 version[0] = ver.majver;
4132 version[1] = ver.minver;
4133 version[2] = ver.patchver;
4134 }
4135
_Section(char const * inName,int64_t start,int64_t size)4136 CrateFile::_Section::_Section(char const *inName, int64_t start, int64_t size)
4137 : start(start), size(size)
4138 {
4139 memset(name, 0, sizeof(name));
4140 if (TF_VERIFY(strlen(inName) <= _SectionNameMaxLength))
4141 strcpy(name, inName);
4142 }
4143
4144 std::ostream &
operator <<(std::ostream & o,ValueRep rep)4145 operator<<(std::ostream &o, ValueRep rep) {
4146 o << "ValueRep enum=" << int(rep.GetType());
4147 if (rep.IsArray())
4148 o << " (array)";
4149 return o << " payload=" << rep.GetPayload();
4150 }
4151
4152 std::ostream &
operator <<(std::ostream & os,TimeSamples const & samples)4153 operator<<(std::ostream &os, TimeSamples const &samples) {
4154 return os << "TimeSamples with " <<
4155 samples.times.Get().size() << " samples";
4156 }
4157
4158 std::ostream &
operator <<(std::ostream & os,Index const & i)4159 operator<<(std::ostream &os, Index const &i) {
4160 return os << i.value;
4161 }
4162
4163 // Size checks for structures written to/read from disk.
4164 static_assert(sizeof(CrateFile::Field) == 16, "");
4165 static_assert(sizeof(CrateFile::Spec) == 12, "");
4166 static_assert(sizeof(CrateFile::Spec_0_0_1) == 16, "");
4167 static_assert(sizeof(_PathItemHeader) == 12, "");
4168 static_assert(sizeof(_PathItemHeader_0_0_1) == 16, "");
4169
4170 } // Usd_CrateFile
4171
4172
4173
4174 PXR_NAMESPACE_CLOSE_SCOPE
4175
4176