WvStreams
|
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 2002 Net Integration Technologies, Inc. 00004 * 00005 * A UniConf hierarchical key path abstraction. 00006 */ 00007 #include "wvassert.h" 00008 #include "wvstream.h" 00009 #include "uniconfkey.h" 00010 #include "wvhash.h" 00011 #include <climits> 00012 #include <assert.h> 00013 #include <strutils.h> 00014 00015 unsigned WvHash(const UniConfKey &k) 00016 { 00017 int numsegs = k.right - k.left; 00018 unsigned result; 00019 switch (numsegs) 00020 { 00021 case 0: 00022 result = 0; 00023 break; 00024 case 1: 00025 result = WvHash(k.store->segments[k.left]); 00026 break; 00027 default: 00028 result = WvHash(k.store->segments[k.left]) 00029 ^ WvHash(k.store->segments[k.right - 1]) 00030 ^ numsegs; 00031 break; 00032 } 00033 return result; 00034 } 00035 00036 // The initial value of 1 for the ref_count of these guarantees 00037 // that they won't ever be deleted 00038 UniConfKey::Store UniConfKey::EMPTY_store(1, 1); 00039 UniConfKey::Store UniConfKey::ANY_store(1, 1, "*"); 00040 UniConfKey::Store UniConfKey::RECURSIVE_ANY_store(1, 1, "..."); 00041 00042 UniConfKey UniConfKey::EMPTY(&EMPTY_store, 0, 0); 00043 UniConfKey UniConfKey::ANY(&ANY_store, 0, 1); 00044 UniConfKey UniConfKey::RECURSIVE_ANY(&RECURSIVE_ANY_store, 0, 1); 00045 00046 00047 UniConfKey::Store::Store(int size, int _ref_count, 00048 WvStringParm key) : 00049 segments(size), 00050 ref_count(_ref_count) 00051 { 00052 if (!key) 00053 return; 00054 00055 WvStringList parts; 00056 parts.split(key, "/"); 00057 00058 segments.resize(parts.count()); 00059 WvStringList::Iter part(parts); 00060 for (part.rewind(); part.next(); ) 00061 { 00062 if (!*part) 00063 continue; 00064 segments.append(*part); 00065 } 00066 if (!!key && key[key.len()-1] == '/' && segments.used() > 0) 00067 segments.append(Segment()); 00068 } 00069 00070 00071 UniConfKey &UniConfKey::collapse() 00072 { 00073 if ((right - left == 1 && !store->segments[right-1]) 00074 || right == left) 00075 { 00076 if (--store->ref_count == 0) 00077 delete store; 00078 store = &EMPTY_store; 00079 left = right = 0; 00080 ++store->ref_count; 00081 } 00082 return *this; 00083 } 00084 00085 00086 void UniConfKey::unique() 00087 { 00088 if (store->ref_count == 1) 00089 return; 00090 store->ref_count--; 00091 Store *old_store = store; 00092 store = new Store(right - left, 1); 00093 for (int i=left; i<right; ++i) 00094 store->segments.append(old_store->segments[i]); 00095 right -= left; 00096 left = 0; 00097 } 00098 00099 UniConfKey::UniConfKey(const UniConfKey &_path, const UniConfKey &_key) : 00100 store(new Store(_path.numsegments() + _key.numsegments() + 1, 1)), 00101 left(0), 00102 right(0) 00103 { 00104 bool hastrailingslash = _key.isempty() || _key.hastrailingslash(); 00105 for (int i=_path.left; i<_path.right; ++i) 00106 { 00107 const Segment &segment = _path.store->segments[i]; 00108 if (!segment) 00109 continue; 00110 store->segments.append(segment); 00111 ++right; 00112 } 00113 for (int j=_key.left; j<_key.right; ++j) 00114 { 00115 const Segment &segment = _key.store->segments[j]; 00116 if (!segment) 00117 continue; 00118 store->segments.append(segment); 00119 ++right; 00120 } 00121 if (hastrailingslash) 00122 { 00123 store->segments.append(""); 00124 ++right; 00125 } 00126 collapse(); 00127 } 00128 00129 00130 void UniConfKey::append(const UniConfKey &_key) 00131 { 00132 bool hastrailingslash = _key.isempty() || _key.hastrailingslash(); 00133 unique(); 00134 store->segments.resize(right - left + _key.right - _key.left + 1); 00135 for (int j=_key.left; j<_key.right; ++j) 00136 { 00137 const Segment &segment = _key.store->segments[j]; 00138 if (!segment) 00139 continue; 00140 store->segments.replace(right, segment); 00141 ++right; 00142 } 00143 if (hastrailingslash) 00144 { 00145 store->segments.replace(right, ""); 00146 ++right; 00147 } 00148 collapse(); 00149 } 00150 00151 00152 void UniConfKey::prepend(const UniConfKey &_key) 00153 { 00154 unique(); 00155 int shift = 0; 00156 for (int j=_key.left; j<_key.right; ++j) 00157 { 00158 if (!!_key.store->segments[j]) 00159 ++shift; 00160 } 00161 store->segments.resize(shift + right - left, shift); 00162 for (int j=_key.left; j<_key.right; ++j) 00163 { 00164 const Segment &segment = _key.store->segments[j]; 00165 if (!segment) 00166 continue; 00167 store->segments.replace(left + j - _key.left, segment); 00168 ++right; 00169 } 00170 collapse(); 00171 } 00172 00173 00174 bool UniConfKey::iswild() const 00175 { 00176 for (int i=left; i<right; ++i) 00177 if (store->segments[i].iswild()) 00178 return true; 00179 return false; 00180 } 00181 00182 00183 UniConfKey UniConfKey::pop(int n) 00184 { 00185 if (n == 0) 00186 return UniConfKey(); 00187 unique(); 00188 if (n > right - left) 00189 n = right - left; 00190 if (n < 0) 00191 n = 0; 00192 int old_left = left; 00193 left += n; 00194 UniConfKey result(store, old_left, left); 00195 collapse(); 00196 return result.collapse(); 00197 } 00198 00199 00200 UniConfKey UniConfKey::range(int i, int j) const 00201 { 00202 if (j > right - left) 00203 j = right - left; 00204 if (i < 0) 00205 i = 0; 00206 if (j < i) 00207 j = i; 00208 return UniConfKey(store, left + i, left + j).collapse(); 00209 } 00210 00211 00212 WvString UniConfKey::printable() const 00213 { 00214 switch (right - left) 00215 { 00216 case 0: 00217 return WvString::empty; 00218 case 1: 00219 return store->segments[left]; 00220 default: 00221 { 00222 WvDynBuf buf; 00223 for (int i=left; i<right; ++i) 00224 { 00225 buf.putstr(store->segments[i]); 00226 if (i < right-1) 00227 buf.put('/'); 00228 } 00229 return buf.getstr(); 00230 } 00231 } 00232 } 00233 00234 00235 int UniConfKey::compareto(const UniConfKey &other) const 00236 { 00237 int i, j; 00238 for (i=left, j=other.left; i<right && j<other.right; ++i, ++j) 00239 { 00240 int val = strcasecmp(store->segments[i], other.store->segments[j]); 00241 if (val != 0) 00242 return val; 00243 } 00244 if (i == right) 00245 { 00246 if (j == other.right) 00247 return 0; 00248 else 00249 return -1; 00250 } 00251 else 00252 return 1; 00253 } 00254 00255 00256 bool UniConfKey::matches(const UniConfKey &pattern) const 00257 { 00258 // TODO: optimize this function 00259 if (*this == pattern) 00260 return true; 00261 00262 UniConfKey head(pattern.first()); 00263 00264 // handle * wildcard 00265 if (head == UniConfKey::ANY) 00266 { 00267 if (isempty()) 00268 return false; 00269 return removefirst().matches(pattern.removefirst()); 00270 } 00271 00272 // handle ... wildcard 00273 if (head == UniConfKey::RECURSIVE_ANY) 00274 { 00275 UniConfKey tail(pattern.removefirst()); 00276 if (tail.isempty()) 00277 return true; // recursively matches anything 00278 for (int n = 0; ; ++n) 00279 { 00280 UniConfKey part(removefirst(n)); 00281 if (part.matches(tail)) 00282 return true; 00283 if (part.isempty()) 00284 break; 00285 } 00286 return false; 00287 } 00288 00289 // no other wildcard arrangements currently supported 00290 return false; 00291 } 00292 00293 00294 bool UniConfKey::suborsame(const UniConfKey &key) const 00295 { 00296 int n = numsegments(); 00297 if (hastrailingslash()) 00298 n -= 1; 00299 00300 if (key.first(n) == first(n)) 00301 return true; 00302 return false; 00303 } 00304 00305 00306 bool UniConfKey::suborsame(const UniConfKey &key, UniConfKey &subkey) const 00307 { 00308 int n = numsegments(); 00309 00310 // Compensate for the directory-style naming convention of the 00311 // trailing slash. 00312 if (hastrailingslash()) 00313 n -= 1; 00314 00315 if (key.first(n) == first(n)) 00316 { 00317 subkey = key.removefirst(n); 00318 return true; 00319 } 00320 return false; 00321 } 00322 00323 00324 UniConfKey UniConfKey::subkey(const UniConfKey &key) const 00325 { 00326 UniConfKey answer; 00327 wvassert(suborsame(key, answer), 00328 "this = '%s'\nkey = '%s'", printable(), key); 00329 return answer; 00330 }