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@209: ArrayMap::Attach(Zone *zone, Handle type) dvander@169: { dvander@169: if (type->newMap()) dvander@169: return ArrayMap::cast(type->newMap()); dvander@169: dvander@169: Local map(zone, ArrayMap::New(zone, type)); dvander@169: if (!map) dvander@169: return NULL; dvander@169: dvander@169: // Ensure the inner type has an ArrayMap if needed. dvander@169: if (type->contained()->isArray()) { dvander@209: Local contained(zone, ArrayType::cast(type->contained())); dvander@169: if (!ArrayMap::Attach(zone, contained)) dvander@169: return NULL; dvander@169: } dvander@169: dvander@169: type->setNewMap(map); dvander@169: return map; dvander@169: } dvander@169: dvander@12: Array * dvander@187: Array::NewRaw(Zone *zone, Handle map, unsigned nelements, Heap::Tenure tenure) dvander@185: { 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@169: Array::NewString(Zone *zone, Handle map, Address buffer, size_t length, Heap::Tenure tenure) dvander@169: { dvander@187: assert(map->type()->isCharArray()); dvander@187: dvander@187: Local array(zone, Array::New1D(zone, map, length, tenure)); dvander@169: if (!array) dvander@169: return NULL; dvander@169: dvander@169: assert(array->length() >= length); dvander@169: memcpy(array->chars(), buffer, length); 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: