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 #include "pxr/pxr.h"
25 #include "pxr/usd/sdf/assetPath.h"
26
27 #include "pxr/base/tf/diagnostic.h"
28 #include "pxr/base/tf/registryManager.h"
29 #include "pxr/base/tf/staticTokens.h"
30 #include "pxr/base/tf/stringUtils.h"
31 #include "pxr/base/tf/type.h"
32
33 #include "pxr/base/vt/array.h"
34 #include "pxr/base/vt/value.h"
35
36 #include <ostream>
37
38 PXR_NAMESPACE_OPEN_SCOPE
39
40 // Register this class with the TfType registry
41 // Array registration included to facilitate Sdf/Types and Sdf/ParserHelpers
TF_REGISTRY_FUNCTION(TfType)42 TF_REGISTRY_FUNCTION(TfType)
43 {
44 TfType::Define<SdfAssetPath>();
45 TfType::Define<VtArray<SdfAssetPath>>();
46 }
47
TF_REGISTRY_FUNCTION(VtValue)48 TF_REGISTRY_FUNCTION(VtValue)
49 {
50 VtValue::RegisterSimpleCast<std::string, SdfAssetPath>();
51 }
52
53 static const char Delimiter = '@';
54
55 // Read a UTF-8 char starting at 'cp' and return its value as an int. Also
56 // advance 'cp' to the start of the next UTF-8 character. If 'cp' does not
57 // point to a valid UTF-8 char, leave 'cp' unmodified and return -1.
58 static int
_ReadUTF8(char const * & cp,std::string * errMsg)59 _ReadUTF8(char const *&cp, std::string *errMsg)
60 {
61 // Return a byte with the high `n` bits set, rest clear.
62 auto highBits = [](int n) {
63 return static_cast<unsigned char>(((1 << n) - 1) << (8 - n));
64 };
65
66 // Return true if `ch` is a continuation byte.
67 auto isContinuation = [&highBits](unsigned char ch) {
68 return (ch & highBits(2)) == highBits(1);
69 };
70
71 // Check for single-character code.
72 if ((*cp & highBits(1)) == 0) {
73 return *cp++;
74 }
75
76 // Check for 2, 3, or 4-byte code.
77 for (int i = 2; i <= 4; ++i) {
78 // This is an N-byte code if the high-order N+1 bits are N 1s
79 // followed by a single 0.
80 if ((*cp & highBits(i + 1)) == highBits(i)) {
81 int ret = *cp & ~highBits(i + 1);
82 // If that's the case then the following N-1 bytes must be
83 // "continuation bytes".
84 for (int j = 1; j != i; ++j) {
85 if (!isContinuation(cp[j])) {
86 char const *ordinalWords[] = {
87 "first", "second", "third", "fourth"
88 };
89 *errMsg = TfStringPrintf("%d-byte UTF-8 code point lacks "
90 "%s continuation byte",
91 i, ordinalWords[j-1]);
92 return -1;
93 }
94 ret = (ret << 6) | (cp[j] & ~highBits(2));
95 }
96 cp += i;
97 return ret;
98 }
99 }
100 *errMsg = TfStringPrintf("invalid UTF-8 code point byte 0x%hhx", *cp);
101 return -1;
102 }
103
104 // Check that the given \p path is valid UTF-8 sans C0 & C1 control characters.
105 // Return true if so. Return false and issue an error indicating the problem if
106 // not.
107 static bool
_ValidateAssetPathString(char const * path)108 _ValidateAssetPathString(char const *path)
109 {
110 // Return true if 'code' is a C0 or C1 control code, false otherwise.
111 auto isControlCode = [](int code) {
112 return ((0x0 <= code && code <= 0x1f) ||
113 code == 0x7f ||
114 (0x80 <= code && code <= 0x9f));
115 };
116
117 char const *cp = path;
118 std::string err;
119 int utf8Char = _ReadUTF8(cp, &err);
120 int charNum = 1;
121 for (; utf8Char > 0; utf8Char = _ReadUTF8(cp, &err)) {
122 if (isControlCode(utf8Char)) {
123 TF_CODING_ERROR("Invalid asset path string -- character %d is "
124 "control character 0x%x",
125 charNum, utf8Char);
126 return false;
127 }
128 ++charNum;
129 }
130 if (utf8Char == -1) {
131 TF_CODING_ERROR("Invalid asset path string -- character %d: %s\n",
132 charNum, err.c_str());
133 return false;
134 }
135 return true;
136 }
137
SdfAssetPath()138 SdfAssetPath::SdfAssetPath()
139 {
140 }
141
SdfAssetPath(const std::string & path)142 SdfAssetPath::SdfAssetPath(const std::string &path)
143 : _assetPath(path)
144 {
145 if (!_ValidateAssetPathString(path.c_str())) {
146 *this = SdfAssetPath();
147 }
148 }
149
SdfAssetPath(const std::string & path,const std::string & resolvedPath)150 SdfAssetPath::SdfAssetPath(const std::string &path,
151 const std::string &resolvedPath)
152 : _assetPath(path)
153 , _resolvedPath(resolvedPath)
154 {
155 if (!_ValidateAssetPathString(path.c_str()) ||
156 !_ValidateAssetPathString(resolvedPath.c_str())) {
157 *this = SdfAssetPath();
158 }
159 }
160
161 bool
operator <(const SdfAssetPath & rhs) const162 SdfAssetPath::operator<(const SdfAssetPath &rhs) const
163 {
164 if (_assetPath < rhs._assetPath)
165 return true;
166 if (rhs._assetPath < _assetPath)
167 return false;
168
169 return _resolvedPath < rhs._resolvedPath;
170 }
171
172 std::ostream&
operator <<(std::ostream & out,const SdfAssetPath & ap)173 operator<<(std::ostream& out, const SdfAssetPath& ap)
174 {
175 return out << Delimiter << ap.GetAssetPath() << Delimiter;
176 }
177
178 PXR_NAMESPACE_CLOSE_SCOPE
179