runtime/array-module.cpp (411 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "array-module.h" #include "builtins.h" #include "handles.h" #include "modules.h" #include "objects.h" #include "runtime.h" #include "symbols.h" #include "thread.h" #include "type-builtins.h" namespace py { static word itemSize(byte typecode) { switch (typecode) { case 'b': case 'B': return kByteSize; case 'u': return kWcharSize; case 'h': case 'H': return kShortSize; case 'i': case 'I': return kIntSize; case 'l': case 'L': return kLongSize; case 'q': case 'Q': return kLongLongSize; case 'f': return kFloatSize; case 'd': return kDoubleSize; default: return -1; } } word arrayByteLength(RawArray array) { byte typecode = SmallStr::cast(array.typecode()).byteAt(0); word item_size = itemSize(typecode); return array.length() * item_size; } RawObject FUNC(array, _array_check)(Thread* thread, Arguments args) { return Bool::fromBool(thread->runtime()->isInstanceOfArray(args.get(0))); } RawObject FUNC(array, _array_new)(Thread* thread, Arguments args) { HandleScope scope(thread); Str typecode_str(&scope, strUnderlying(args.get(1))); DCHECK(typecode_str.length() == 1, "typecode must be a single-char str"); byte typecode = typecode_str.byteAt(0); word item_size = itemSize(typecode); if (item_size == -1) { return thread->raiseWithFmt( LayoutId::kValueError, "bad typecode (must be b, B, u, h, H, i, I, l, L, q, Q, f or d)"); } word len = SmallInt::cast(args.get(2)).value() * item_size; Runtime* runtime = thread->runtime(); Type array_type(&scope, args.get(0)); Layout layout(&scope, array_type.instanceLayout()); Array result(&scope, runtime->newInstance(layout)); result.setTypecode(*typecode_str); result.setLength(0); result.setBuffer(runtime->mutableBytesWith(len, 0)); return *result; } static bool isIntTypecode(char typecode) { switch (typecode) { case 'f': FALLTHROUGH; case 'd': FALLTHROUGH; case 'u': return false; default: return true; } } static RawObject raiseOverflowError(Thread* thread, CastError error) { if (error == CastError::Underflow) { return thread->raiseWithFmt(LayoutId::kOverflowError, "less than minimum"); } DCHECK(error == CastError::Overflow, "Only two forms of CastErrors"); return thread->raiseWithFmt(LayoutId::kOverflowError, "greater than maximum"); } // TODO(T67799743): Abstract out integer cases to int-builtins.cpp for reuse // with memoryviews static RawObject packObject(Thread* thread, uword address, char typecode, word index, RawObject value) { byte* dst = reinterpret_cast<byte*>(address + index); if (isIntTypecode(typecode)) { if (!value.isInt()) return Unbound::object(); switch (typecode) { case 'b': { OptInt<char> opt_val = RawInt::cast(value).asInt<char>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'h': { OptInt<short> opt_val = RawInt::cast(value).asInt<short>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'i': { OptInt<int> opt_val = RawInt::cast(value).asInt<int>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'l': { OptInt<long> opt_val = RawInt::cast(value).asInt<long>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'B': { OptInt<unsigned char> opt_val = RawInt::cast(value).asInt<unsigned char>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'H': { OptInt<unsigned short> opt_val = RawInt::cast(value).asInt<unsigned short>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'I': { OptInt<unsigned int> opt_val = RawInt::cast(value).asInt<unsigned int>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'L': { OptInt<unsigned long> opt_val = RawInt::cast(value).asInt<unsigned long>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'q': { OptInt<long long> opt_val = RawInt::cast(value).asInt<long long>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } case 'Q': { OptInt<unsigned long long> opt_val = RawInt::cast(value).asInt<unsigned long long>(); if (opt_val.error != CastError::None) { return raiseOverflowError(thread, opt_val.error); } std::memcpy(dst, &opt_val.value, sizeof(opt_val.value)); break; } } return NoneType::object(); } Runtime* runtime = thread->runtime(); switch (typecode) { case 'f': { if (!runtime->isInstanceOfFloat(value)) return Unbound::object(); float value_float = Float::cast(floatUnderlying(value)).value(); std::memcpy(dst, &value_float, sizeof(value_float)); return NoneType::object(); } case 'd': { if (!runtime->isInstanceOfFloat(value)) return Unbound::object(); double value_double = Float::cast(floatUnderlying(value)).value(); std::memcpy(dst, &value_double, sizeof(value_double)); return NoneType::object(); } case 'u': UNIMPLEMENTED("array.__setitem__ with unicode is unimplemented"); default: UNREACHABLE("invalid typecode"); } return NoneType::object(); } // TODO(T67799743): Abstract out integer cases to int-builtins.cpp for reuse // with memoryviews static RawObject unpackObject(Thread* thread, uword address, char format, word index) { Runtime* runtime = thread->runtime(); byte* src = reinterpret_cast<byte*>(address + index); switch (format) { case 'b': return RawSmallInt::fromWord(Utils::readBytes<signed char>(src)); case 'B': return RawSmallInt::fromWord(Utils::readBytes<unsigned char>(src)); case 'h': return RawSmallInt::fromWord(Utils::readBytes<short>(src)); case 'H': return RawSmallInt::fromWord(Utils::readBytes<unsigned short>(src)); case 'i': return runtime->newInt(Utils::readBytes<int>(src)); case 'I': return runtime->newInt(Utils::readBytes<unsigned int>(src)); case 'l': return runtime->newInt(Utils::readBytes<long>(src)); case 'L': return runtime->newIntFromUnsigned(Utils::readBytes<unsigned long>(src)); case 'q': return runtime->newInt(Utils::readBytes<long long>(src)); case 'Q': return runtime->newIntFromUnsigned( Utils::readBytes<unsigned long long>(src)); case 'f': return runtime->newFloat(Utils::readBytes<float>(src)); case 'd': return runtime->newFloat(Utils::readBytes<double>(src)); case 'u': UNIMPLEMENTED("array.__getitem__ with unicode is unimplemented"); default: UNREACHABLE("invalid format"); } } RawObject FUNC(array, _array_getitem)(Thread* thread, Arguments args) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object self_obj(&scope, args.get(0)); if (!runtime->isInstanceOfArray(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(array)); } Array array(&scope, *self_obj); Object index_obj(&scope, args.get(1)); if (!runtime->isInstanceOfInt(*index_obj)) { return Unbound::object(); } word index = intUnderlying(*index_obj).asWordSaturated(); if (!SmallInt::isValid(index)) { return thread->raiseWithFmt(LayoutId::kIndexError, "cannot fit '%T' into an index-sized integer", &index_obj); } word length = array.length(); if (index < 0) { index = length - index; } if (index < 0 || index >= length) { return thread->raiseWithFmt(LayoutId::kIndexError, "array index out of range"); } char typecode = Str::cast(array.typecode()).byteAt(0); word item_size = itemSize(typecode); word byte_index; if (__builtin_mul_overflow(index, item_size, &byte_index)) { return thread->raiseWithFmt(LayoutId::kIndexError, "array index out of range"); } return unpackObject(thread, MutableBytes::cast(array.buffer()).address(), typecode, byte_index); } RawObject FUNC(array, _array_setitem)(Thread* thread, Arguments args) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object self_obj(&scope, args.get(0)); if (!runtime->isInstanceOfArray(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(array)); } Array array(&scope, *self_obj); Object index_obj(&scope, args.get(1)); if (!runtime->isInstanceOfInt(*index_obj)) { return Unbound::object(); } word index = intUnderlying(*index_obj).asWordSaturated(); if (!SmallInt::isValid(index)) { return thread->raiseWithFmt(LayoutId::kIndexError, "cannot fit '%T' into an index-sized integer", &index_obj); } word length = array.length(); if (index < 0) { index = length - index; } if (index < 0 || index >= length) { return thread->raiseWithFmt(LayoutId::kIndexError, "array assignment index out of range"); } char typecode = Str::cast(array.typecode()).byteAt(0); word item_size = itemSize(typecode); word byte_index; if (__builtin_mul_overflow(index, item_size, &byte_index)) { return thread->raiseWithFmt(LayoutId::kIndexError, "array assignment index out of range"); } return packObject(thread, MutableBytes::cast(array.buffer()).address(), typecode, byte_index, args.get(2)); } static void arrayEnsureCapacity(Thread* thread, const Array& array, word min_length) { DCHECK_BOUND(min_length, SmallInt::kMaxValue); HandleScope scope(thread); MutableBytes buffer(&scope, array.buffer()); word curr_length = buffer.length(); if (min_length <= curr_length) return; word new_length = Runtime::newCapacity(curr_length, min_length); MutableBytes new_buffer( &scope, thread->runtime()->newMutableBytesUninitialized(new_length)); new_buffer.replaceFromWith(0, *buffer, curr_length); new_buffer.replaceFromWithByte(curr_length, 0, new_length - curr_length); array.setBuffer(*new_buffer); } RawObject FUNC(array, _array_reserve)(Thread* thread, Arguments args) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object array_obj(&scope, args.get(0)); if (!runtime->isInstanceOfArray(*array_obj)) { return thread->raiseRequiresType(array_obj, ID(array)); } Array array(&scope, *array_obj); word item_size = itemSize(Str::cast(array.typecode()).byteAt(0)); word size = intUnderlying(args.get(1)).asWord() * item_size; arrayEnsureCapacity(thread, array, size); return NoneType::object(); } RawObject FUNC(array, _array_append)(Thread* thread, Arguments args) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object self_obj(&scope, args.get(0)); if (!runtime->isInstanceOfArray(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(array)); } Array array(&scope, *self_obj); char typecode = Str::cast(array.typecode()).byteAt(0); word item_size = itemSize(typecode); word length = array.length(); // This shouldn't overflow, since length is limited to a SmallInt word new_length = length + 1; word new_capacity; if (__builtin_mul_overflow(new_length, item_size, &new_capacity)) { return thread->raiseWithFmt(LayoutId::kIndexError, "array assignment index out of range"); } arrayEnsureCapacity(thread, array, new_capacity); MutableBytes buffer(&scope, array.buffer()); Object result(&scope, packObject(thread, buffer.address(), typecode, new_capacity - item_size, args.get(1))); if (!result.isErrorException() && !result.isUnbound()) { array.setLength(new_length); } return *result; } RawObject FUNC(array, _array_repeat)(Thread* thread, Arguments args) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object self_obj(&scope, args.get(0)); if (!runtime->isInstanceOfArray(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(array)); } Array self(&scope, args.get(0)); Object count_obj(&scope, args.get(1)); if (!runtime->isInstanceOfInt(*count_obj)) { return Unbound::object(); } word count = intUnderlying(*count_obj).asWordSaturated(); word byte_length = arrayByteLength(*self); word new_capacity; if (__builtin_mul_overflow(byte_length, count, &new_capacity) || !SmallInt::isValid(new_capacity)) { return thread->raiseWithFmt(LayoutId::kMemoryError, "repeated array is too long"); } Bytes buffer(&scope, self.buffer()); MutableBytes new_buffer( &scope, runtime->bytesRepeat(thread, buffer, byte_length, count)); Layout layout(&scope, runtime->layoutAt(LayoutId::kArray)); Array result(&scope, runtime->newInstance(layout)); result.setTypecode(self.typecode()); result.setBuffer(*new_buffer); result.setLength(self.length() * count); return *result; } RawObject METH(array, __len__)(Thread* thread, Arguments args) { HandleScope scope(thread); Object self_obj(&scope, args.get(0)); if (!thread->runtime()->isInstanceOfArray(*self_obj)) { return thread->raiseRequiresType(self_obj, ID(array)); } Array self(&scope, args.get(0)); return SmallInt::fromWord(self.length()); } static const BuiltinAttribute kArrayAttributes[] = { {ID(_array__buffer), RawArray::kBufferOffset, AttributeFlags::kHidden}, {ID(_array__length), RawArray::kLengthOffset, AttributeFlags::kHidden}, {ID(typecode), RawArray::kTypecodeOffset, AttributeFlags::kReadOnly}, }; void initializeArrayType(Thread* thread) { addBuiltinType(thread, ID(array), LayoutId::kArray, /*superclass_id=*/LayoutId::kObject, kArrayAttributes, Array::kSize, /*basetype=*/true); } } // namespace py