this repo has no description
at trunk 280 lines 10 kB view raw
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 2#include "attributedict.h" 3 4#include "runtime.h" 5#include "thread.h" 6#include "type-builtins.h" 7 8namespace py { 9 10const word kInitialCapacity = 16; 11 12static NEVER_INLINE void attributeGrow(Thread* thread, 13 const AttributeDict& attrs) { 14 HandleScope scope(thread); 15 Tuple old_data(&scope, attrs.attributes()); 16 17 // Count the number of filled buckets that are not tombstones. 18 word old_capacity = old_data.length(); 19 word num_items = 0; 20 for (word idx = 0; idx < old_capacity; idx += kAttributeBucketNumWords) { 21 RawObject key = old_data.at(idx + kAttributeBucketKeyOffset); 22 if (key != kAttributeDictEmptyKey && key != kAttributeDictTombstoneKey) { 23 num_items++; 24 } 25 } 26 27 // Grow if more than half of the buckets are filled, otherwise maintain size 28 // and just clean out the tombstones. 29 word old_num_buckets = old_capacity >> 1; 30 word new_capacity = old_capacity; 31 if (num_items > (old_num_buckets >> 1)) { 32 // Grow if more than half of the buckets are filled, otherwise just clean 33 // out the tombstones. 34 new_capacity *= 2; 35 } 36 37 // Allocate new tuple and re-hash. 38 MutableTuple new_data(&scope, 39 thread->runtime()->newMutableTuple(new_capacity)); 40 word num_buckets = new_capacity >> 1; 41 DCHECK(Utils::isPowerOfTwo(num_buckets), "must be power of two"); 42 word new_remaining = (num_buckets * 2) / 3; 43 word mask = num_buckets - 1; 44 Object key(&scope, NoneType::object()); 45 for (word old_idx = 0; old_idx < old_capacity; 46 old_idx += kAttributeBucketNumWords) { 47 key = old_data.at(old_idx + kAttributeBucketKeyOffset); 48 if (key == kAttributeDictEmptyKey || key == kAttributeDictTombstoneKey) { 49 continue; 50 } 51 DCHECK(key.isStr(), "key must be None, _Unbound or str"); 52 word hash = internedStrHash(*key); 53 word bucket = hash & mask; 54 word num_probes = 0; 55 while (new_data.at(bucket * kAttributeBucketNumWords + 56 kAttributeBucketKeyOffset) != kAttributeDictEmptyKey) { 57 num_probes++; 58 bucket = (bucket + num_probes) & mask; 59 } 60 new_data.atPut( 61 bucket * kAttributeBucketNumWords + kAttributeBucketKeyOffset, *key); 62 new_data.atPut( 63 bucket * kAttributeBucketNumWords + kAttributeBucketValueOffset, 64 old_data.at(old_idx + kAttributeBucketValueOffset)); 65 new_remaining--; 66 } 67 DCHECK(new_remaining > 0, "must have remaining buckets"); 68 attrs.setAttributes(*new_data); 69 attrs.setAttributesRemaining(new_remaining); 70} 71 72void attributeDictInit(Thread* thread, const AttributeDict& attrs) { 73 attrs.setAttributes(thread->runtime()->newMutableTuple(kInitialCapacity)); 74 word num_buckets = kInitialCapacity >> 1; 75 attrs.setAttributesRemaining((num_buckets * 2) / 3); 76} 77 78RawObject attributeKeys(Thread* thread, const AttributeDict& attrs) { 79 HandleScope scope(thread); 80 MutableTuple data(&scope, attrs.attributes()); 81 Runtime* runtime = thread->runtime(); 82 List keys(&scope, runtime->newList()); 83 Object key(&scope, NoneType::object()); 84 Object cell(&scope, NoneType::object()); 85 for (word i = 0, length = data.length(); i < length; 86 i += kAttributeBucketNumWords) { 87 key = data.at(i + kAttributeBucketKeyOffset); 88 if (key == kAttributeDictEmptyKey || key == kAttributeDictTombstoneKey) { 89 continue; 90 } 91 DCHECK(key.isStr(), "key must be a str"); 92 cell = data.at(i + kAttributeBucketValueOffset); 93 if (ValueCell::cast(*cell).isPlaceholder()) { 94 continue; 95 } 96 runtime->listAdd(thread, keys, key); 97 } 98 return *keys; 99} 100 101word attributeLen(Thread* thread, const AttributeDict& attrs) { 102 HandleScope scope(thread); 103 MutableTuple data(&scope, attrs.attributes()); 104 Object key(&scope, NoneType::object()); 105 Object cell(&scope, NoneType::object()); 106 word count = 0; 107 for (word i = 0, length = data.length(); i < length; 108 i += kAttributeBucketNumWords) { 109 key = data.at(i + kAttributeBucketKeyOffset); 110 if (key == kAttributeDictEmptyKey || key == kAttributeDictTombstoneKey) { 111 continue; 112 } 113 DCHECK(key.isStr(), "key must be a str"); 114 cell = data.at(i + kAttributeBucketValueOffset); 115 if (ValueCell::cast(*cell).isPlaceholder()) { 116 continue; 117 } 118 count++; 119 } 120 return count; 121} 122 123RawObject attributeName(Thread* thread, const Object& name_obj) { 124 if (name_obj.isSmallStr()) { 125 return *name_obj; 126 } 127 if (name_obj.isLargeStr()) { 128 return Runtime::internLargeStr(thread, name_obj); 129 } 130 131 Runtime* runtime = thread->runtime(); 132 if (!runtime->isInstanceOfStr(*name_obj)) { 133 return thread->raiseWithFmt(LayoutId::kTypeError, 134 "attribute name must be string, not '%T'", 135 &name_obj); 136 } 137 HandleScope scope(thread); 138 Type type(&scope, runtime->typeOf(*name_obj)); 139 if (typeLookupInMroById(thread, *type, ID(__eq__)) != 140 runtime->strDunderEq() || 141 typeLookupInMroById(thread, *type, ID(__hash__)) != 142 runtime->strDunderHash()) { 143 UNIMPLEMENTED( 144 "str subclasses with __eq__ or __hash__ not supported as attribute " 145 "name"); 146 } 147 Str name_str(&scope, strUnderlying(*name_obj)); 148 return Runtime::internStr(thread, name_str); 149} 150 151RawObject attributeNameNoException(Thread* thread, const Object& name_obj) { 152 if (name_obj.isSmallStr()) { 153 return *name_obj; 154 } 155 if (name_obj.isLargeStr()) { 156 return Runtime::internLargeStr(thread, name_obj); 157 } 158 159 Runtime* runtime = thread->runtime(); 160 if (!runtime->isInstanceOfStr(*name_obj)) { 161 return Error::error(); 162 } 163 HandleScope scope(thread); 164 Type type(&scope, runtime->typeOf(*name_obj)); 165 if (typeLookupInMroById(thread, *type, ID(__eq__)) != 166 runtime->strDunderEq() || 167 typeLookupInMroById(thread, *type, ID(__hash__)) != 168 runtime->strDunderHash()) { 169 UNIMPLEMENTED( 170 "str subclasses with __eq__ or __hash__ not supported as attribute " 171 "name"); 172 } 173 Str name_str(&scope, strUnderlying(*name_obj)); 174 return Runtime::internStr(thread, name_str); 175} 176 177bool attributeFindForRemoval(const AttributeDict& attrs, const Object& name, 178 Object* value_cell_out, word* index_out) { 179 RawMutableTuple data = MutableTuple::cast(attrs.attributes()); 180 word hash = internedStrHash(*name); 181 word mask = (data.length() - 1) >> 1; 182 for (word bucket = hash & mask, num_probes = 0;; 183 bucket = (bucket + ++num_probes) & mask) { 184 word index = bucket * kAttributeBucketNumWords; 185 RawObject key = data.at(index + kAttributeBucketKeyOffset); 186 if (key == *name) { 187 *value_cell_out = data.at(index + kAttributeBucketValueOffset); 188 *index_out = index; 189 return true; 190 } 191 if (key == kAttributeDictEmptyKey) { 192 return false; 193 } 194 // Remaining cases are either a key that does not match or tombstone. 195 } 196} 197 198void attributeRemove(const AttributeDict& attrs, word index) { 199 RawMutableTuple data = MutableTuple::cast(attrs.attributes()); 200 data.atPut(index + kAttributeBucketKeyOffset, kAttributeDictTombstoneKey); 201 data.atPut(index + kAttributeBucketValueOffset, NoneType::object()); 202} 203 204RawObject inline USED attributeValueCellAtPut(Thread* thread, 205 const AttributeDict& attrs, 206 const Object& name) { 207 HandleScope scope(thread); 208 MutableTuple data_obj(&scope, attrs.attributes()); 209 RawMutableTuple data = *data_obj; 210 word hash = internedStrHash(*name); 211 word mask = (data.length() - 1) >> 1; 212 for (word bucket = hash & mask, num_probes = 0, last_tombstone = -1;; 213 bucket = (bucket + ++num_probes) & mask) { 214 word idx = bucket * kAttributeBucketNumWords; 215 RawObject key = data.at(idx + kAttributeBucketKeyOffset); 216 if (key == *name) { 217 return RawValueCell::cast(data.at(idx + kAttributeBucketValueOffset)); 218 } 219 if (key == kAttributeDictEmptyKey) { 220 DCHECK(Runtime::isInternedStr(thread, name), "expected interned str"); 221 RawValueCell cell = ValueCell::cast(thread->runtime()->newValueCell()); 222 cell.makePlaceholder(); 223 // newValueCell() may have triggered a GC; restore raw references. 224 data = *data_obj; 225 if (last_tombstone >= 0) { 226 // Overwrite an existing tombstone entry. 227 word last_tombstone_idx = last_tombstone * kAttributeBucketNumWords; 228 data.atPut(last_tombstone_idx + kAttributeBucketKeyOffset, *name); 229 data.atPut(last_tombstone_idx + kAttributeBucketValueOffset, cell); 230 } else { 231 // Use new bucket. 232 data.atPut(idx + kAttributeBucketKeyOffset, *name); 233 data.atPut(idx + kAttributeBucketValueOffset, cell); 234 word remaining = attrs.attributesRemaining() - 1; 235 attrs.setAttributesRemaining(remaining); 236 if (remaining == 0) { 237 ValueCell cell_obj(&scope, cell); 238 attributeGrow(thread, attrs); 239 return *cell_obj; 240 } 241 } 242 return cell; 243 } 244 if (key == kAttributeDictTombstoneKey) { 245 last_tombstone = bucket; 246 } 247 } 248} 249 250bool attributeValueCellAt(RawAttributeDict attrs, RawObject name, 251 RawObject* result_out) { 252 word hash = internedStrHash(name); 253 return attributeValueCellAtWithHash(attrs, name, hash, result_out); 254} 255 256RawObject attributeValues(Thread* thread, const AttributeDict& attrs) { 257 HandleScope scope(thread); 258 MutableTuple data(&scope, attrs.attributes()); 259 Runtime* runtime = thread->runtime(); 260 List values(&scope, runtime->newList()); 261 Object key(&scope, NoneType::object()); 262 Object value(&scope, NoneType::object()); 263 for (word i = 0, length = data.length(); i < length; 264 i += kAttributeBucketNumWords) { 265 key = data.at(i + kAttributeBucketKeyOffset); 266 if (key == kAttributeDictEmptyKey || key == kAttributeDictTombstoneKey) { 267 continue; 268 } 269 DCHECK(key.isStr(), "key must be a str"); 270 value = data.at(i + kAttributeBucketValueOffset); 271 if (ValueCell::cast(*value).isPlaceholder()) { 272 continue; 273 } 274 value = ValueCell::cast(*value).value(); 275 runtime->listAdd(thread, values, value); 276 } 277 return *values; 278} 279 280} // namespace py