src/Array.cpp
author David Anderson <dvander@alliedmods.net>
Sun Sep 16 21:34:10 2012 -0700 (2012-09-16)
changeset 188 bab29bcec7fe
parent 187 9308d52205a0
child 209 5cbec15fadfe
permissions -rw-r--r--
Restructure Array so the elements vector can be replaced.
     1 /* vim: set ts=4 sw=4 tw=99 et:
     2  *
     3  * Copyright (C) 2012 David Anderson
     4  *
     5  * This file is part of SourcePawn.
     6  *
     7  * SourcePawn is free software: you can redistribute it and/or modify it under
     8  * the terms of the GNU General Public License as published by the Free
     9  * Software Foundation, either version 3 of the License, or (at your option)
    10  * any later version.
    11  * 
    12  * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY
    13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License along with
    17  * SourcePawn. If not, see http://www.gnu.org/licenses/.
    18  */
    19 #include <string.h>
    20 #include "Array.h"
    21 #include "FixedArray.h"
    22 #include "Heap.h"
    23 #include "Zone.h"
    24 #include "Structures.h"
    25 #include "Heap-inl.h"
    26 
    27 using namespace ke;
    28 
    29 ArrayMap *
    30 ArrayMap::New(Zone *zone, Handle<Type> type)
    31 {
    32     Local<Map> obj(zone, zone->allocateMap(MapKind_Array, sizeof(ArrayMap), MapKind_MetaArray));
    33     if (!obj)
    34         return NULL;
    35 
    36     Local<ArrayMap> map(zone, ArrayMap::cast(obj));
    37     map->type_ = type;
    38     return map;
    39 }
    40 
    41 ArrayMap *
    42 ArrayMap::Attach(Zone *zone, Handle<Type> type)
    43 {
    44     if (type->newMap())
    45         return ArrayMap::cast(type->newMap());
    46 
    47     Local<ArrayMap> map(zone, ArrayMap::New(zone, type));
    48     if (!map)
    49         return NULL;
    50 
    51     // Ensure the inner type has an ArrayMap if needed.
    52     if (type->contained()->isArray()) {
    53         Local<Type> contained(zone, type->contained());
    54         if (!ArrayMap::Attach(zone, contained))
    55             return NULL;
    56     }
    57 
    58     type->setNewMap(map);
    59     return map;
    60 }
    61 
    62 Array *
    63 Array::NewRaw(Zone *zone, Handle<ArrayMap> map, unsigned nelements, Heap::Tenure tenure)
    64 {
    65     if (nelements > (INT_MAX / SizeForElement(map->type()))) {
    66         zone->reportError(Message_ArraySizeTooBig);
    67         return NULL;
    68     }
    69 
    70     size_t bytes = sizeof(Array);
    71     if (map->type()->hasInlineElements())
    72         bytes += SizeForElement(map->type()) * nelements;
    73 
    74     Local<Array> array(zone, Array::cast(zone->allocate(map, bytes, tenure)));
    75     if (!array)
    76         return NULL;
    77 
    78     array->length_ = 0;
    79     array->elements_ = NULL;
    80 
    81     if (map->type()->hasInlineElements()) {
    82         // This must be a fixed array containing no traceables.
    83         assert(nelements > 0);
    84         assert(map->type()->isFixedLength());
    85         assert(!map->type()->contained()->isTraceable());
    86 
    87         // Compute an interior |elements_| pointer, such that if elements_
    88         // is accessed like a FixedArray, we reach the appropriate vector.
    89         array->elements_ = Address((Array *)array + 1) - ByteArray::offsetToElements();
    90         array->length_ = nelements;
    91         memset(array->bytes(), 0, SizeForElement(map->type()) * nelements);
    92         return array;
    93     }
    94 
    95     // If there is nothing to allocate, we don't bother creating elements yet.
    96     if (!nelements)
    97         return array;
    98 
    99     if (!map->type()->contained()->isTraceable()) {
   100         size_t nbytes = SizeForElement(map->type()) * nelements;
   101         Local<ByteArray> elements(zone, ByteArray::New(zone, nbytes, tenure));
   102         if (!elements)
   103             return NULL;
   104 
   105         array->updateLengthAndElements(zone, nelements, elements);
   106         return array;
   107     }
   108 
   109     Local<FixedArray> elements(zone, FixedArray::New(zone, nelements, tenure));
   110     if (!elements)
   111         return NULL;
   112 
   113     array->updateLengthAndElements(zone, nelements, elements);
   114 
   115     // If this is an array of structs, we're required to allocate those structs now.
   116     if (map->type()->contained()->isStruct()) {
   117         Local<StructMap> childMap(zone, StructMap::cast(map->type()->contained()->newMap()));
   118         Local<Struct> child(zone);
   119         for (unsigned i = 0; i < nelements; i++) {
   120             child = Struct::New(zone, childMap, tenure);
   121             if (!child)
   122                 return NULL;
   123 
   124             elements->set(zone, i, child);
   125         }
   126     }
   127 
   128     return array;
   129 }
   130 
   131 static Array *
   132 ConstructSizedArray(Zone *zone, Handle<ArrayMap> map, int dims[MAX_ARRAY_DEPTH],
   133                     unsigned level, Heap::Tenure tenure)
   134 {
   135     Local<Array> array(zone, Array::NewRaw(zone, map, dims[level], tenure));
   136     if (!array)
   137         return NULL;
   138 
   139     if (map->type()->contained()->isArray()) {
   140         Local<ArrayMap> childMap(zone, ArrayMap::cast(map->type()->contained()->newMap()));
   141         Local<Array> child(zone);
   142 
   143         for (unsigned i = 0; i < unsigned(dims[level]); i++) {
   144             child = ConstructSizedArray(zone, childMap, dims, level + 1, tenure);
   145             if (!child)
   146                 return NULL;
   147 
   148             array->writeObject(zone, i, child);
   149         }
   150     }
   151 
   152     return array;
   153 }
   154 
   155 Array *
   156 Array::NewSized(Zone *zone, Handle<ArrayMap> map, int dims[MAX_ARRAY_DEPTH], Heap::Tenure tenure)
   157 {
   158     for (size_t i = 0; i < map->type()->levels(); i++) {
   159         if (dims[i] < 0) {
   160             zone->reportError(Message_InvalidDimensionSize, dims[i], i);
   161             return NULL;
   162         }
   163     }
   164 
   165     return ConstructSizedArray(zone, map, dims, 0, tenure);
   166 }
   167 
   168 static Array *
   169 ConstructFixedArray(Zone *zone, Handle<ArrayMap> map, Heap::Tenure tenure)
   170 {
   171     unsigned length = map->type()->fixedLength();
   172     Local<Array> array(zone, Array::NewRaw(zone, map, length, tenure));
   173     if (!array)
   174         return NULL;
   175 
   176     if (map->type()->contained()->isArray()) {
   177         Local<ArrayMap> childMap(zone, ArrayMap::cast(map->type()->contained()->newMap()));
   178         Local<Array> child(zone);
   179 
   180         if (childMap->type()->isFixedLength()) {
   181             for (unsigned i = 0; i < length; i++) {
   182                 child = ConstructFixedArray(zone, childMap, tenure);
   183                 if (!child)
   184                     return NULL;
   185 
   186                 array->writeObject(zone, i, child);
   187             }
   188         } else {
   189             for (unsigned i = 0; i < length; i++) {
   190                 child = Array::NewRaw(zone, childMap, 0, tenure);
   191                 if (!child)
   192                     return NULL;
   193 
   194                 array->writeObject(zone, i, child);
   195             }
   196         }
   197     }
   198 
   199     return array;
   200 }
   201 
   202 Array *
   203 Array::NewFixed(Zone *zone, Handle<ArrayMap> map, Heap::Tenure tenure)
   204 {
   205     return ConstructFixedArray(zone, map, tenure);
   206 }
   207 
   208 Array *
   209 Array::NewString(Zone *zone, Handle<ArrayMap> map, Address buffer, size_t length, Heap::Tenure tenure)
   210 {
   211     assert(map->type()->isCharArray());
   212 
   213     Local<Array> array(zone, Array::New1D(zone, map, length, tenure));
   214     if (!array)
   215         return NULL;
   216 
   217     assert(array->length() >= length);
   218     memcpy(array->chars(), buffer, length);
   219     return array;
   220 }
   221 
   222 Array *
   223 Array::New1D(Zone *zone, Handle<ArrayMap> map, uint32 elements, Heap::Tenure tenure)
   224 {
   225     assert(map->type()->levels());
   226     return Array::NewRaw(zone, map, elements, tenure);
   227 }
   228 
   229 Array *
   230 Array::NewEmpty(Zone *zone, Handle<ArrayMap> map, Heap::Tenure tenure)
   231 {
   232     assert(!map->type()->isFixedLength());
   233 
   234     return Array::NewRaw(zone, map, 0, tenure);
   235 }
   236 
   237 Array *
   238 Array::NewExternal(Zone *zone, Handle<ArrayMap> map, void *buffer, unsigned length,
   239                    Heap::Tenure tenure)
   240 {
   241     Local<Array> array(zone, Array::cast(zone->allocate(map, sizeof(Array), tenure)));
   242     if (!array)
   243         return NULL;
   244 
   245     array->length_ = length;
   246     array->elements_ = Address(buffer) - ByteArray::offsetToElements();
   247     return array;
   248 }
   249