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, &timesRep, &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