this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "array-module.h"
3
4#include "builtins.h"
5#include "handles.h"
6#include "modules.h"
7#include "objects.h"
8#include "runtime.h"
9#include "symbols.h"
10#include "thread.h"
11#include "type-builtins.h"
12
13namespace py {
14
15static word itemSize(byte typecode) {
16 switch (typecode) {
17 case 'b':
18 case 'B':
19 return kByteSize;
20 case 'u':
21 return kWcharSize;
22 case 'h':
23 case 'H':
24 return kShortSize;
25 case 'i':
26 case 'I':
27 return kIntSize;
28 case 'l':
29 case 'L':
30 return kLongSize;
31 case 'q':
32 case 'Q':
33 return kLongLongSize;
34 case 'f':
35 return kFloatSize;
36 case 'd':
37 return kDoubleSize;
38 default:
39 return -1;
40 }
41}
42
43word arrayByteLength(RawArray array) {
44 byte typecode = SmallStr::cast(array.typecode()).byteAt(0);
45 word item_size = itemSize(typecode);
46 return array.length() * item_size;
47}
48
49RawObject FUNC(array, _array_check)(Thread* thread, Arguments args) {
50 return Bool::fromBool(thread->runtime()->isInstanceOfArray(args.get(0)));
51}
52
53RawObject FUNC(array, _array_new)(Thread* thread, Arguments args) {
54 HandleScope scope(thread);
55 Str typecode_str(&scope, strUnderlying(args.get(1)));
56 DCHECK(typecode_str.length() == 1, "typecode must be a single-char str");
57 byte typecode = typecode_str.byteAt(0);
58 word item_size = itemSize(typecode);
59 if (item_size == -1) {
60 return thread->raiseWithFmt(
61 LayoutId::kValueError,
62 "bad typecode (must be b, B, u, h, H, i, I, l, L, q, Q, f or d)");
63 }
64 word len = SmallInt::cast(args.get(2)).value() * item_size;
65 Runtime* runtime = thread->runtime();
66
67 Type array_type(&scope, args.get(0));
68 Layout layout(&scope, array_type.instanceLayout());
69 Array result(&scope, runtime->newInstance(layout));
70 result.setTypecode(*typecode_str);
71 result.setLength(0);
72 result.setBuffer(runtime->mutableBytesWith(len, 0));
73 return *result;
74}
75
76static bool isIntTypecode(char typecode) {
77 switch (typecode) {
78 case 'f':
79 FALLTHROUGH;
80 case 'd':
81 FALLTHROUGH;
82 case 'u':
83 return false;
84 default:
85 return true;
86 }
87}
88
89static RawObject raiseOverflowError(Thread* thread, CastError error) {
90 if (error == CastError::Underflow) {
91 return thread->raiseWithFmt(LayoutId::kOverflowError, "less than minimum");
92 }
93 DCHECK(error == CastError::Overflow, "Only two forms of CastErrors");
94 return thread->raiseWithFmt(LayoutId::kOverflowError, "greater than maximum");
95}
96
97// TODO(T67799743): Abstract out integer cases to int-builtins.cpp for reuse
98// with memoryviews
99static RawObject packObject(Thread* thread, uword address, char typecode,
100 word index, RawObject value) {
101 byte* dst = reinterpret_cast<byte*>(address + index);
102 if (isIntTypecode(typecode)) {
103 if (!value.isInt()) return Unbound::object();
104 switch (typecode) {
105 case 'b': {
106 OptInt<char> opt_val = RawInt::cast(value).asInt<char>();
107 if (opt_val.error != CastError::None) {
108 return raiseOverflowError(thread, opt_val.error);
109 }
110 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
111 break;
112 }
113 case 'h': {
114 OptInt<short> opt_val = RawInt::cast(value).asInt<short>();
115 if (opt_val.error != CastError::None) {
116 return raiseOverflowError(thread, opt_val.error);
117 }
118 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
119 break;
120 }
121 case 'i': {
122 OptInt<int> opt_val = RawInt::cast(value).asInt<int>();
123 if (opt_val.error != CastError::None) {
124 return raiseOverflowError(thread, opt_val.error);
125 }
126 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
127 break;
128 }
129 case 'l': {
130 OptInt<long> opt_val = RawInt::cast(value).asInt<long>();
131 if (opt_val.error != CastError::None) {
132 return raiseOverflowError(thread, opt_val.error);
133 }
134 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
135 break;
136 }
137 case 'B': {
138 OptInt<unsigned char> opt_val =
139 RawInt::cast(value).asInt<unsigned char>();
140 if (opt_val.error != CastError::None) {
141 return raiseOverflowError(thread, opt_val.error);
142 }
143 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
144 break;
145 }
146 case 'H': {
147 OptInt<unsigned short> opt_val =
148 RawInt::cast(value).asInt<unsigned short>();
149 if (opt_val.error != CastError::None) {
150 return raiseOverflowError(thread, opt_val.error);
151 }
152 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
153 break;
154 }
155 case 'I': {
156 OptInt<unsigned int> opt_val =
157 RawInt::cast(value).asInt<unsigned int>();
158 if (opt_val.error != CastError::None) {
159 return raiseOverflowError(thread, opt_val.error);
160 }
161 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
162 break;
163 }
164 case 'L': {
165 OptInt<unsigned long> opt_val =
166 RawInt::cast(value).asInt<unsigned long>();
167 if (opt_val.error != CastError::None) {
168 return raiseOverflowError(thread, opt_val.error);
169 }
170 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
171 break;
172 }
173 case 'q': {
174 OptInt<long long> opt_val = RawInt::cast(value).asInt<long long>();
175 if (opt_val.error != CastError::None) {
176 return raiseOverflowError(thread, opt_val.error);
177 }
178 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
179 break;
180 }
181 case 'Q': {
182 OptInt<unsigned long long> opt_val =
183 RawInt::cast(value).asInt<unsigned long long>();
184 if (opt_val.error != CastError::None) {
185 return raiseOverflowError(thread, opt_val.error);
186 }
187 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
188 break;
189 }
190 }
191 return NoneType::object();
192 }
193
194 Runtime* runtime = thread->runtime();
195 switch (typecode) {
196 case 'f': {
197 if (!runtime->isInstanceOfFloat(value)) return Unbound::object();
198 float value_float = Float::cast(floatUnderlying(value)).value();
199 std::memcpy(dst, &value_float, sizeof(value_float));
200 return NoneType::object();
201 }
202
203 case 'd': {
204 if (!runtime->isInstanceOfFloat(value)) return Unbound::object();
205 double value_double = Float::cast(floatUnderlying(value)).value();
206 std::memcpy(dst, &value_double, sizeof(value_double));
207 return NoneType::object();
208 }
209
210 case 'u':
211 UNIMPLEMENTED("array.__setitem__ with unicode is unimplemented");
212 default:
213 UNREACHABLE("invalid typecode");
214 }
215 return NoneType::object();
216}
217
218// TODO(T67799743): Abstract out integer cases to int-builtins.cpp for reuse
219// with memoryviews
220static RawObject unpackObject(Thread* thread, uword address, char format,
221 word index) {
222 Runtime* runtime = thread->runtime();
223 byte* src = reinterpret_cast<byte*>(address + index);
224 switch (format) {
225 case 'b':
226 return RawSmallInt::fromWord(Utils::readBytes<signed char>(src));
227 case 'B':
228 return RawSmallInt::fromWord(Utils::readBytes<unsigned char>(src));
229 case 'h':
230 return RawSmallInt::fromWord(Utils::readBytes<short>(src));
231 case 'H':
232 return RawSmallInt::fromWord(Utils::readBytes<unsigned short>(src));
233 case 'i':
234 return runtime->newInt(Utils::readBytes<int>(src));
235 case 'I':
236 return runtime->newInt(Utils::readBytes<unsigned int>(src));
237 case 'l':
238 return runtime->newInt(Utils::readBytes<long>(src));
239 case 'L':
240 return runtime->newIntFromUnsigned(Utils::readBytes<unsigned long>(src));
241 case 'q':
242 return runtime->newInt(Utils::readBytes<long long>(src));
243 case 'Q':
244 return runtime->newIntFromUnsigned(
245 Utils::readBytes<unsigned long long>(src));
246 case 'f':
247 return runtime->newFloat(Utils::readBytes<float>(src));
248 case 'd':
249 return runtime->newFloat(Utils::readBytes<double>(src));
250 case 'u':
251 UNIMPLEMENTED("array.__getitem__ with unicode is unimplemented");
252 default:
253 UNREACHABLE("invalid format");
254 }
255}
256
257RawObject FUNC(array, _array_getitem)(Thread* thread, Arguments args) {
258 HandleScope scope(thread);
259 Runtime* runtime = thread->runtime();
260 Object self_obj(&scope, args.get(0));
261 if (!runtime->isInstanceOfArray(*self_obj)) {
262 return thread->raiseRequiresType(self_obj, ID(array));
263 }
264 Array array(&scope, *self_obj);
265
266 Object index_obj(&scope, args.get(1));
267 if (!runtime->isInstanceOfInt(*index_obj)) {
268 return Unbound::object();
269 }
270 word index = intUnderlying(*index_obj).asWordSaturated();
271 if (!SmallInt::isValid(index)) {
272 return thread->raiseWithFmt(LayoutId::kIndexError,
273 "cannot fit '%T' into an index-sized integer",
274 &index_obj);
275 }
276 word length = array.length();
277 if (index < 0) {
278 index = length - index;
279 }
280 if (index < 0 || index >= length) {
281 return thread->raiseWithFmt(LayoutId::kIndexError,
282 "array index out of range");
283 }
284 char typecode = Str::cast(array.typecode()).byteAt(0);
285 word item_size = itemSize(typecode);
286 word byte_index;
287 if (__builtin_mul_overflow(index, item_size, &byte_index)) {
288 return thread->raiseWithFmt(LayoutId::kIndexError,
289 "array index out of range");
290 }
291 return unpackObject(thread, MutableBytes::cast(array.buffer()).address(),
292 typecode, byte_index);
293}
294
295RawObject FUNC(array, _array_setitem)(Thread* thread, Arguments args) {
296 HandleScope scope(thread);
297 Runtime* runtime = thread->runtime();
298 Object self_obj(&scope, args.get(0));
299 if (!runtime->isInstanceOfArray(*self_obj)) {
300 return thread->raiseRequiresType(self_obj, ID(array));
301 }
302 Array array(&scope, *self_obj);
303
304 Object index_obj(&scope, args.get(1));
305 if (!runtime->isInstanceOfInt(*index_obj)) {
306 return Unbound::object();
307 }
308 word index = intUnderlying(*index_obj).asWordSaturated();
309 if (!SmallInt::isValid(index)) {
310 return thread->raiseWithFmt(LayoutId::kIndexError,
311 "cannot fit '%T' into an index-sized integer",
312 &index_obj);
313 }
314 word length = array.length();
315 if (index < 0) {
316 index = length - index;
317 }
318 if (index < 0 || index >= length) {
319 return thread->raiseWithFmt(LayoutId::kIndexError,
320 "array assignment index out of range");
321 }
322 char typecode = Str::cast(array.typecode()).byteAt(0);
323 word item_size = itemSize(typecode);
324 word byte_index;
325 if (__builtin_mul_overflow(index, item_size, &byte_index)) {
326 return thread->raiseWithFmt(LayoutId::kIndexError,
327 "array assignment index out of range");
328 }
329 return packObject(thread, MutableBytes::cast(array.buffer()).address(),
330 typecode, byte_index, args.get(2));
331}
332
333static void arrayEnsureCapacity(Thread* thread, const Array& array,
334 word min_length) {
335 DCHECK_BOUND(min_length, SmallInt::kMaxValue);
336 HandleScope scope(thread);
337 MutableBytes buffer(&scope, array.buffer());
338 word curr_length = buffer.length();
339 if (min_length <= curr_length) return;
340 word new_length = Runtime::newCapacity(curr_length, min_length);
341 MutableBytes new_buffer(
342 &scope, thread->runtime()->newMutableBytesUninitialized(new_length));
343 new_buffer.replaceFromWith(0, *buffer, curr_length);
344 new_buffer.replaceFromWithByte(curr_length, 0, new_length - curr_length);
345 array.setBuffer(*new_buffer);
346}
347
348RawObject FUNC(array, _array_reserve)(Thread* thread, Arguments args) {
349 HandleScope scope(thread);
350 Runtime* runtime = thread->runtime();
351 Object array_obj(&scope, args.get(0));
352 if (!runtime->isInstanceOfArray(*array_obj)) {
353 return thread->raiseRequiresType(array_obj, ID(array));
354 }
355 Array array(&scope, *array_obj);
356 word item_size = itemSize(Str::cast(array.typecode()).byteAt(0));
357 word size = intUnderlying(args.get(1)).asWord() * item_size;
358 arrayEnsureCapacity(thread, array, size);
359 return NoneType::object();
360}
361
362RawObject FUNC(array, _array_append)(Thread* thread, Arguments args) {
363 HandleScope scope(thread);
364 Runtime* runtime = thread->runtime();
365 Object self_obj(&scope, args.get(0));
366 if (!runtime->isInstanceOfArray(*self_obj)) {
367 return thread->raiseRequiresType(self_obj, ID(array));
368 }
369 Array array(&scope, *self_obj);
370 char typecode = Str::cast(array.typecode()).byteAt(0);
371 word item_size = itemSize(typecode);
372 word length = array.length();
373 // This shouldn't overflow, since length is limited to a SmallInt
374 word new_length = length + 1;
375 word new_capacity;
376 if (__builtin_mul_overflow(new_length, item_size, &new_capacity)) {
377 return thread->raiseWithFmt(LayoutId::kIndexError,
378 "array assignment index out of range");
379 }
380
381 arrayEnsureCapacity(thread, array, new_capacity);
382 MutableBytes buffer(&scope, array.buffer());
383 Object result(&scope, packObject(thread, buffer.address(), typecode,
384 new_capacity - item_size, args.get(1)));
385 if (!result.isErrorException() && !result.isUnbound()) {
386 array.setLength(new_length);
387 }
388 return *result;
389}
390
391RawObject FUNC(array, _array_repeat)(Thread* thread, Arguments args) {
392 HandleScope scope(thread);
393 Runtime* runtime = thread->runtime();
394 Object self_obj(&scope, args.get(0));
395 if (!runtime->isInstanceOfArray(*self_obj)) {
396 return thread->raiseRequiresType(self_obj, ID(array));
397 }
398 Array self(&scope, args.get(0));
399 Object count_obj(&scope, args.get(1));
400 if (!runtime->isInstanceOfInt(*count_obj)) {
401 return Unbound::object();
402 }
403 word count = intUnderlying(*count_obj).asWordSaturated();
404 word byte_length = arrayByteLength(*self);
405 word new_capacity;
406 if (__builtin_mul_overflow(byte_length, count, &new_capacity) ||
407 !SmallInt::isValid(new_capacity)) {
408 return thread->raiseWithFmt(LayoutId::kMemoryError,
409 "repeated array is too long");
410 }
411 Bytes buffer(&scope, self.buffer());
412 MutableBytes new_buffer(
413 &scope, runtime->bytesRepeat(thread, buffer, byte_length, count));
414 Layout layout(&scope, runtime->layoutAt(LayoutId::kArray));
415 Array result(&scope, runtime->newInstance(layout));
416 result.setTypecode(self.typecode());
417 result.setBuffer(*new_buffer);
418 result.setLength(self.length() * count);
419 return *result;
420}
421
422RawObject METH(array, __len__)(Thread* thread, Arguments args) {
423 HandleScope scope(thread);
424 Object self_obj(&scope, args.get(0));
425 if (!thread->runtime()->isInstanceOfArray(*self_obj)) {
426 return thread->raiseRequiresType(self_obj, ID(array));
427 }
428 Array self(&scope, args.get(0));
429 return SmallInt::fromWord(self.length());
430}
431
432static const BuiltinAttribute kArrayAttributes[] = {
433 {ID(_array__buffer), RawArray::kBufferOffset, AttributeFlags::kHidden},
434 {ID(_array__length), RawArray::kLengthOffset, AttributeFlags::kHidden},
435 {ID(typecode), RawArray::kTypecodeOffset, AttributeFlags::kReadOnly},
436};
437
438void initializeArrayType(Thread* thread) {
439 addBuiltinType(thread, ID(array), LayoutId::kArray,
440 /*superclass_id=*/LayoutId::kObject, kArrayAttributes,
441 Array::kSize, /*basetype=*/true);
442}
443
444} // namespace py