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