dvander@154: /* vim: set ts=4 sw=4 tw=99 et: dvander@154: * dvander@12: * Copyright (C) 2012 David Anderson dvander@12: * dvander@154: * This file is part of SourcePawn. dvander@12: * dvander@154: * SourcePawn is free software: you can redistribute it and/or modify it under dvander@12: * the terms of the GNU General Public License as published by the Free dvander@12: * Software Foundation, either version 3 of the License, or (at your option) dvander@12: * any later version. dvander@12: * dvander@154: * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY dvander@12: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@12: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. dvander@12: * dvander@12: * You should have received a copy of the GNU General Public License along with dvander@154: * SourcePawn. If not, see http://www.gnu.org/licenses/. dvander@12: */ danderson@91: #include dvander@12: #include "Array.h" dvander@188: #include "FixedArray.h" dvander@12: #include "Heap.h" dvander@12: #include "Zone.h" dvander@154: #include "Structures.h" dvander@12: #include "Heap-inl.h" dvander@12: dvander@59: using namespace ke; dvander@12: dvander@12: ArrayMap * dvander@209: ArrayMap::New(Zone *zone, Handle type) dvander@12: { dvander@31: Local obj(zone, zone->allocateMap(MapKind_Array, sizeof(ArrayMap), MapKind_MetaArray)); dvander@12: if (!obj) dvander@12: return NULL; dvander@12: dvander@31: Local map(zone, ArrayMap::cast(obj)); dvander@13: map->type_ = type; dvander@12: return map; dvander@12: } dvander@12: dvander@169: ArrayMap * dvander@247: ArrayMap::NewDependent(Zone *zone, Handle type) dvander@247: { dvander@247: Local obj(zone, zone->allocateMap(MapKind_DependentArray, sizeof(ArrayMap), MapKind_MetaArray)); dvander@247: if (!obj) dvander@247: return NULL; dvander@247: dvander@247: Local map(zone, ArrayMap::cast(obj)); dvander@247: map->type_ = type; dvander@247: return map; dvander@247: } dvander@247: dvander@12: Array * dvander@187: Array::NewRaw(Zone *zone, Handle map, unsigned nelements, Heap::Tenure tenure) dvander@185: { dvander@234: assert(!map->type()->isFixedLength() || nelements == map->type()->fixedLength()); dvander@234: dvander@188: if (nelements > (INT_MAX / SizeForElement(map->type()))) { dvander@187: zone->reportError(Message_ArraySizeTooBig); dvander@187: return NULL; dvander@187: } dvander@185: dvander@188: size_t bytes = sizeof(Array); dvander@188: if (map->type()->hasInlineElements()) dvander@188: bytes += SizeForElement(map->type()) * nelements; dvander@188: dvander@31: Local array(zone, Array::cast(zone->allocate(map, bytes, tenure))); dvander@31: if (!array) dvander@12: return NULL; dvander@12: dvander@188: array->length_ = 0; dvander@188: array->elements_ = NULL; dvander@188: dvander@188: if (map->type()->hasInlineElements()) { dvander@188: // This must be a fixed array containing no traceables. dvander@188: assert(nelements > 0); dvander@188: assert(map->type()->isFixedLength()); dvander@188: assert(!map->type()->contained()->isTraceable()); dvander@188: dvander@188: // Compute an interior |elements_| pointer, such that if elements_ dvander@188: // is accessed like a FixedArray, we reach the appropriate vector. dvander@188: array->elements_ = Address((Array *)array + 1) - ByteArray::offsetToElements(); dvander@188: array->length_ = nelements; dvander@188: memset(array->bytes(), 0, SizeForElement(map->type()) * nelements); dvander@188: return array; dvander@188: } dvander@154: dvander@188: // If there is nothing to allocate, we don't bother creating elements yet. dvander@188: if (!nelements) dvander@188: return array; dvander@188: dvander@188: if (!map->type()->contained()->isTraceable()) { dvander@188: size_t nbytes = SizeForElement(map->type()) * nelements; dvander@188: Local elements(zone, ByteArray::New(zone, nbytes, tenure)); dvander@188: if (!elements) dvander@188: return NULL; dvander@188: dvander@188: array->updateLengthAndElements(zone, nelements, elements); dvander@188: return array; dvander@188: } dvander@188: dvander@188: Local elements(zone, FixedArray::New(zone, nelements, tenure)); dvander@188: if (!elements) dvander@188: return NULL; dvander@188: dvander@188: array->updateLengthAndElements(zone, nelements, elements); dvander@188: dvander@188: // If this is an array of structs, we're required to allocate those structs now. dvander@187: if (map->type()->contained()->isStruct()) { dvander@209: Local childType(zone, StructType::cast(map->type()->contained())); dvander@209: Local childMap(zone, childType->newMap()); dvander@154: Local child(zone); dvander@187: for (unsigned i = 0; i < nelements; i++) { dvander@154: child = Struct::New(zone, childMap, tenure); dvander@154: if (!child) dvander@154: return NULL; dvander@154: dvander@188: elements->set(zone, i, child); dvander@154: } dvander@187: } dvander@187: dvander@187: return array; dvander@187: } dvander@187: dvander@187: static Array * dvander@187: ConstructSizedArray(Zone *zone, Handle map, int dims[MAX_ARRAY_DEPTH], dvander@187: unsigned level, Heap::Tenure tenure) dvander@187: { dvander@187: Local array(zone, Array::NewRaw(zone, map, dims[level], tenure)); dvander@187: if (!array) dvander@187: return NULL; dvander@187: dvander@187: if (map->type()->contained()->isArray()) { dvander@209: Local childType(zone, ArrayType::cast(map->type()->contained())); dvander@209: Local childMap(zone, childType->newMap()); dvander@187: Local child(zone); dvander@187: dvander@187: for (unsigned i = 0; i < unsigned(dims[level]); i++) { dvander@187: child = ConstructSizedArray(zone, childMap, dims, level + 1, tenure); dvander@187: if (!child) dvander@187: return NULL; dvander@187: dvander@187: array->writeObject(zone, i, child); dvander@187: } dvander@187: } dvander@187: dvander@187: return array; dvander@187: } dvander@187: dvander@187: Array * dvander@187: Array::NewSized(Zone *zone, Handle map, int dims[MAX_ARRAY_DEPTH], Heap::Tenure tenure) dvander@187: { dvander@187: for (size_t i = 0; i < map->type()->levels(); i++) { dvander@187: if (dims[i] < 0) { dvander@187: zone->reportError(Message_InvalidDimensionSize, dims[i], i); dvander@187: return NULL; dvander@187: } dvander@154: } dvander@187: dvander@187: return ConstructSizedArray(zone, map, dims, 0, tenure); dvander@187: } dvander@187: dvander@187: static Array * dvander@187: ConstructFixedArray(Zone *zone, Handle map, Heap::Tenure tenure) dvander@187: { dvander@187: unsigned length = map->type()->fixedLength(); dvander@187: Local array(zone, Array::NewRaw(zone, map, length, tenure)); dvander@187: if (!array) dvander@187: return NULL; dvander@187: dvander@187: if (map->type()->contained()->isArray()) { dvander@209: Local childType(zone, ArrayType::cast(map->type()->contained())); dvander@209: Local childMap(zone, childType->newMap()); dvander@187: Local child(zone); dvander@187: dvander@187: if (childMap->type()->isFixedLength()) { dvander@187: for (unsigned i = 0; i < length; i++) { dvander@187: child = ConstructFixedArray(zone, childMap, tenure); dvander@187: if (!child) dvander@187: return NULL; dvander@187: dvander@187: array->writeObject(zone, i, child); dvander@187: } dvander@187: } else { dvander@187: for (unsigned i = 0; i < length; i++) { dvander@187: child = Array::NewRaw(zone, childMap, 0, tenure); dvander@187: if (!child) dvander@187: return NULL; dvander@187: dvander@187: array->writeObject(zone, i, child); dvander@187: } dvander@187: } dvander@187: } dvander@187: dvander@31: return array; dvander@12: } dvander@31: dvander@65: Array * dvander@187: Array::NewFixed(Zone *zone, Handle map, Heap::Tenure tenure) dvander@187: { dvander@187: return ConstructFixedArray(zone, map, tenure); dvander@187: } dvander@187: dvander@187: Array * dvander@234: Array::NewString(Zone *zone, Handle map, char *buffer, size_t length, Heap::Tenure tenure) dvander@169: { dvander@187: assert(map->type()->isCharArray()); dvander@187: dvander@234: Local array(zone); dvander@234: dvander@234: if (map->type()->isFixedLength()) dvander@234: array = Array::NewFixed(zone, map, tenure); dvander@234: else dvander@234: array = Array::New1D(zone, map, length + 1, tenure); dvander@169: if (!array) dvander@169: return NULL; dvander@169: dvander@234: assert(array->length() >= length + 1); dvander@234: memcpy(array->chars(), buffer, length + 1); dvander@169: return array; dvander@169: } dvander@169: dvander@169: Array * dvander@187: Array::New1D(Zone *zone, Handle map, uint32 elements, Heap::Tenure tenure) dvander@187: { dvander@187: assert(map->type()->levels()); dvander@187: return Array::NewRaw(zone, map, elements, tenure); dvander@187: } dvander@187: dvander@187: Array * dvander@187: Array::NewEmpty(Zone *zone, Handle map, Heap::Tenure tenure) dvander@187: { dvander@187: assert(!map->type()->isFixedLength()); dvander@187: dvander@187: return Array::NewRaw(zone, map, 0, tenure); dvander@187: } dvander@187: dvander@187: Array * dvander@188: Array::NewExternal(Zone *zone, Handle map, void *buffer, unsigned length, dvander@65: Heap::Tenure tenure) dvander@65: { dvander@65: Local array(zone, Array::cast(zone->allocate(map, sizeof(Array), tenure))); dvander@65: if (!array) dvander@65: return NULL; dvander@65: dvander@188: array->length_ = length; dvander@188: array->elements_ = Address(buffer) - ByteArray::offsetToElements(); dvander@65: return array; dvander@65: } dvander@154: dvander@234: static bool dvander@234: CopyArrayElements(Zone *zone, Handle contained, Handle dest, Handle source) dvander@234: { dvander@234: if (contained->isTraceable()) { dvander@234: if (contained->isStruct()) { dvander@234: assert(dest->type()->contained() == contained); dvander@234: assert(source->length() == dest->length()); dvander@234: dvander@234: Local left(zone); dvander@234: Local right(zone); dvander@234: for (unsigned i = 0; i < source->length(); i++) { dvander@234: left = Struct::cast(dest->readObject(i)); dvander@234: right = Struct::cast(source->readObject(i)); dvander@234: if (!Struct::Copy(zone, left, right)) dvander@234: return false; dvander@234: } dvander@236: } else if (contained->isArray() && ArrayType::cast(contained)->isFixedLength()) { dvander@236: assert(ArrayType::cast(source->type())->fixedLength() == dvander@236: ArrayType::cast(contained)->fixedLength()); dvander@236: dvander@236: Local left(zone); dvander@236: Local right(zone); dvander@236: for (unsigned i = 0; i < source->length(); i++) { dvander@236: left = Array::cast(dest->readObject(i)); dvander@236: right = Array::cast(source->readObject(i)); dvander@236: if (!Array::ShallowCopy(zone, left, right)) dvander@236: return false; dvander@236: } dvander@234: } else { dvander@234: assert(dest->length() == source->length()); dvander@234: for (unsigned i = 0; i < source->length(); i++) dvander@234: dest->writeObject(zone, i, source->readObject(i)); dvander@234: } dvander@234: } else { dvander@234: assert(contained->pod() == dest->type()->contained()->pod()); dvander@234: assert(contained->pod() == PrimitiveType_Char dvander@234: ? source->length() <= dest->length() dvander@234: : source->length() == dest->length()); dvander@234: dvander@234: assert(dest->length()); dvander@234: memcpy(dest->bytes(), source->bytes(), source->length() * Array::SizeForElement(dest->type())); dvander@234: } dvander@234: return true; dvander@234: } dvander@234: dvander@234: // Shallow copy an array. Fixed slots in the destination are copied from dvander@234: // the source, but otherwise, references are assigned. dvander@234: bool dvander@234: Array::ShallowCopy(Zone *zone, Handle dest, Handle source) dvander@234: { dvander@236: Local contained(zone, dest->type()->contained()); dvander@234: dvander@236: return CopyArrayElements(zone, contained, dest, source); dvander@234: } dvander@234: dvander@234: Array * dvander@234: Array::DeepCopy(Zone *zone, Handle source, Heap::Tenure tenure) dvander@234: { dvander@234: Local map(zone, ArrayMap::cast(source->map())); dvander@234: dvander@234: Local dest(zone, Array::NewRaw(zone, map, source->length(), tenure)); dvander@234: if (!dest) dvander@234: return NULL; dvander@234: dvander@234: Local contained(zone, source->type()->contained()); dvander@234: if (contained->isArray()) { dvander@234: assert(false); dvander@234: Local sub(zone); dvander@234: for (size_t i = 0; i < source->length(); i++) { dvander@234: sub = Array::cast(source->readObject(i)); dvander@234: sub = DeepCopy(zone, sub, tenure); dvander@234: if (!sub) dvander@234: return NULL; dvander@234: dest->writeObject(zone, i, sub); dvander@234: } dvander@234: } else { dvander@234: if (!CopyArrayElements(zone, contained, dest, source)) dvander@234: return NULL; dvander@234: } dvander@234: dvander@234: return dest; dvander@234: } dvander@247: dvander@247: Array * dvander@247: DependentArray::New(Zone *zone, Handle source, Handle map, unsigned index) dvander@247: { dvander@247: Local sourcetype(zone, source->type()); dvander@247: dvander@247: // Right now we only support char arrays, to avoid nasty deep cloning dvander@247: // and to maybe cut off this feature as far as we can. dvander@247: assert(sourcetype->isCharArray()); dvander@247: assert(map->type()->isCharArray()); dvander@247: dvander@247: // We allow taking 0-length dependent strings. It's safe because any reads dvander@247: // or writes will fail. dvander@247: assert(index <= source->length()); dvander@247: dvander@247: unsigned newlen = source->length() - index; dvander@247: unsigned eltsize = SizeForElement(sourcetype); dvander@247: dvander@247: if (!sourcetype->isFixedLength()) { dvander@247: // Simple case, just copy the array. dvander@247: Local map(zone, sourcetype->newMap()); dvander@247: dvander@247: Local dest(zone, Array::NewRaw(zone, map, newlen, Heap::Tenure_Default)); dvander@247: if (!dest) dvander@247: return NULL; dvander@247: dvander@247: memcpy(dest->bytes(), dvander@247: (Address)source->bytes() + (index * eltsize), dvander@247: newlen * eltsize); dvander@247: return dest; dvander@247: } dvander@247: dvander@247: Local array(zone, DependentArray::cast(zone->allocate(map, sizeof(DependentArray), Heap::Tenure_Default))); dvander@247: if (!array) dvander@247: return NULL; dvander@247: dvander@247: // :GC: This is currently not safe for moving GC! Although we will mark dvander@247: // the object owning the interior pointer, the interior pointer could be dvander@247: // moved later. We will eventually need some way of dealing with this. dvander@247: Address elements = Address(source->bytes()) + (index * eltsize); dvander@247: dvander@247: array->root_ = source; dvander@247: array->length_ = newlen; dvander@247: array->elements_ = elements - ByteArray::offsetToElements(); dvander@247: return array; dvander@247: }