source: trunk/abcl/src/org/armedbear/lisp/ComplexString.java

Last change on this file was 13720, checked in by ehuelsmann, 12 years ago

String hash randomization.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.8 KB
Line 
1/*
2 * ComplexString.java
3 *
4 * Copyright (C) 2002-2007 Peter Graves
5 * $Id: ComplexString.java 13720 2012-01-05 21:56:44Z ehuelsmann $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38public final class ComplexString extends AbstractString
39{
40  private int capacity;
41  private int fillPointer = -1; // -1 indicates no fill pointer.
42  private boolean isDisplaced;
43
44  // For non-displaced arrays.
45  private char[] chars;
46
47  // For displaced arrays.
48  private AbstractArray array;
49  private int displacement;
50
51  public ComplexString(int capacity)
52  {
53    this.capacity = capacity;
54    chars = new char[capacity];
55    isDisplaced = false;
56  }
57
58  public ComplexString(int capacity, AbstractArray array, int displacement)
59  {
60    this.capacity = capacity;
61    this.array = array;
62    this.displacement = displacement;
63    isDisplaced = true;
64  }
65
66  @Override
67  public LispObject typeOf()
68  {
69    return list(Symbol.STRING, number(capacity()));
70  }
71
72  @Override
73  public LispObject classOf()
74  {
75    return BuiltInClass.STRING;
76  }
77
78  @Override
79  public boolean hasFillPointer()
80  {
81    return fillPointer >= 0;
82  }
83
84  @Override
85  public int getFillPointer()
86  {
87    return fillPointer;
88  }
89
90  @Override
91  public void setFillPointer(int n)
92  {
93    fillPointer = n;
94  }
95
96  @Override
97  public void setFillPointer(LispObject obj)
98  {
99    if (obj == T)
100      fillPointer = capacity();
101    else
102      {
103        int n = Fixnum.getValue(obj);
104        if (n > capacity())
105          {
106            StringBuffer sb = new StringBuffer("The new fill pointer (");
107            sb.append(n);
108            sb.append(") exceeds the capacity of the vector (");
109            sb.append(capacity());
110            sb.append(").");
111            error(new LispError(sb.toString()));
112          }
113        else if (n < 0)
114          {
115            StringBuffer sb = new StringBuffer("The new fill pointer (");
116            sb.append(n);
117            sb.append(") is negative.");
118            error(new LispError(sb.toString()));
119          }
120        else
121          fillPointer = n;
122      }
123  }
124
125  @Override
126  public boolean isDisplaced()
127  {
128    return isDisplaced;
129  }
130
131  @Override
132  public LispObject arrayDisplacement()
133  {
134    LispObject value1, value2;
135    if (array != null)
136      {
137        value1 = array;
138        value2 = Fixnum.getInstance(displacement);
139      }
140    else
141      {
142        value1 = NIL;
143        value2 = Fixnum.ZERO;
144      }
145    return LispThread.currentThread().setValues(value1, value2);
146  }
147
148  @Override
149  public char[] chars()
150  {
151    if (chars != null)
152      return chars;
153    Debug.assertTrue(array != null);
154    char[] copy = new char[capacity];
155    if (array instanceof AbstractString)
156      System.arraycopy(array.chars(), displacement, copy, 0, capacity);
157    else if (array.getElementType() == Symbol.CHARACTER)
158      {
159        for (int i = 0; i < capacity; i++)
160          {
161            LispObject obj = array.AREF(displacement + i);
162            copy[i] = LispCharacter.getValue(obj);
163          }
164      }
165    else
166      type_error(array, Symbol.STRING);
167    return copy;
168  }
169
170  @Override
171  public char[] getStringChars()
172  {
173    if (fillPointer < 0)
174      return chars();
175    char[] ret = new char[fillPointer];
176    System.arraycopy(chars(), 0, ret, 0, fillPointer);
177    return ret;
178  }
179
180  @Override
181  public boolean equal(LispObject obj)
182  {
183    if (this == obj)
184      return true;
185    if (obj instanceof AbstractString)
186      {
187        AbstractString string = (AbstractString) obj;
188        if (string.length() != length())
189          return false;
190        for (int i = length(); i-- > 0;)
191          if (string.charAt(i) != charAt(i))
192            return false;
193        return true;
194      }
195    if (obj instanceof NilVector)
196      return obj.equal(this);
197    return false;
198  }
199
200  @Override
201  public boolean equalp(LispObject obj)
202  {
203    if (this == obj)
204      return true;
205    if (obj instanceof AbstractString)
206      {
207        AbstractString string = (AbstractString) obj;
208        if (string.length() != length())
209          return false;
210        for (int i = length(); i-- > 0;)
211          {
212            if (string.charAt(i) != charAt(i))
213              {
214                if (LispCharacter.toLowerCase(string.charAt(i)) != LispCharacter.toLowerCase(charAt(i)))
215                  return false;
216              }
217          }
218        return true;
219      }
220    if (obj instanceof AbstractBitVector)
221      return false;
222    if (obj instanceof AbstractArray)
223      return obj.equalp(this);
224    return false;
225  }
226
227  @Override
228  public LispObject subseq(int start, int end)
229  {
230    SimpleString s = new SimpleString(end - start);
231    int i = start, j = 0;
232    while (i < end)
233      s.setCharAt(j++, charAt(i++));
234    return s;
235  }
236
237  @Override
238  public void fill(LispObject obj)
239  {
240    fill(LispCharacter.getValue(obj));
241  }
242
243  @Override
244  public void fill(char c)
245  {
246    for (int i = length(); i-- > 0;)
247      setCharAt(i, c);
248  }
249
250  @Override
251  public void shrink(int n)
252  {
253    if (chars != null)
254      {
255        if (n < capacity)
256          {
257            char[] newArray = new char[n];
258            System.arraycopy(chars, 0, newArray, 0, n);
259            chars = newArray;
260            capacity = n;
261            fillPointer = -1;
262            return;
263          }
264        if (n == capacity)
265          return;
266      }
267    Debug.assertTrue(chars == null);
268    // Displaced array. Copy existing characters.
269    chars = new char[n];
270    if (array instanceof AbstractString)
271      {
272        AbstractString string = (AbstractString) array;
273        for (int i = 0; i < n; i++)
274          {
275            chars[i] = string.charAt(displacement + i);
276          }
277      }
278    else
279      {
280        for (int i = 0; i < n; i++)
281          {
282            LispCharacter character =
283              (LispCharacter) array.AREF(displacement + i);
284            chars[i] = character.value;
285          }
286      }
287    capacity = n;
288    array = null;
289    displacement = 0;
290    isDisplaced = false;
291    fillPointer = -1;
292  }
293
294  @Override
295  public LispObject reverse()
296  {
297    int length = length();
298    SimpleString result = new SimpleString(length);
299    int i, j;
300    for (i = 0, j = length - 1; i < length; i++, j--)
301      result.setCharAt(i, charAt(j));
302    return result;
303  }
304
305  @Override
306  public LispObject nreverse()
307  {
308    int i = 0;
309    int j = length() - 1;
310    while (i < j)
311      {
312        char temp = charAt(i);
313        setCharAt(i, charAt(j));
314        setCharAt(j, temp);
315        ++i;
316        --j;
317      }
318    return this;
319  }
320
321  @Override
322  public String getStringValue()
323  {
324    if (fillPointer >= 0)
325      return new String(chars(), 0, fillPointer);
326    else
327      return new String(chars());
328  }
329
330  @Override
331  public Object javaInstance()
332  {
333    return new String(getStringValue());
334  }
335
336  @Override
337  public Object javaInstance(Class c)
338  {
339    return javaInstance();
340  }
341
342  @Override
343  public final int capacity()
344  {
345    return capacity;
346  }
347
348  @Override
349  public final int length()
350  {
351    return fillPointer >= 0 ? fillPointer : capacity;
352  }
353
354  @Override
355  public char charAt(int index)
356  {
357    if (chars != null)
358      {
359        try
360          {
361            return chars[index];
362          }
363        catch (ArrayIndexOutOfBoundsException e)
364          {
365            badIndex(index, capacity);
366            return 0; // Not reached.
367          }
368      }
369    else
370      return LispCharacter.getValue(array.AREF(index + displacement));
371  }
372
373  @Override
374  public void setCharAt(int index, char c)
375  {
376    if (chars != null)
377      {
378        try
379          {
380            chars[index] = c;
381          }
382        catch (ArrayIndexOutOfBoundsException e)
383          {
384            badIndex(index, capacity);
385          }
386      }
387    else
388      array.aset(index + displacement, LispCharacter.getInstance(c));
389  }
390
391  @Override
392  public LispObject elt(int index)
393  {
394    final int limit = length();
395    if (index < 0 || index >= limit)
396      badIndex(index, limit);
397    return LispCharacter.getInstance(charAt(index));
398  }
399
400  // Ignores fill pointer.
401  @Override
402  public LispObject CHAR(int index)
403  {
404    return LispCharacter.getInstance(charAt(index));
405  }
406
407  // Ignores fill pointer.
408  @Override
409  public LispObject AREF(int index)
410  {
411    return LispCharacter.getInstance(charAt(index));
412  }
413
414  @Override
415  public void aset(int index, LispObject newValue)
416  {
417      setCharAt(index, LispCharacter.getValue(newValue));
418  }
419
420  @Override
421  public void vectorPushExtend(LispObject element)
422
423  {
424    if (fillPointer < 0)
425      noFillPointer();
426    if (fillPointer >= capacity)
427      {
428        // Need to extend vector.
429        ensureCapacity(capacity * 2 + 1);
430      }
431    if (chars != null)
432      {
433        chars[fillPointer] = LispCharacter.getValue(element);
434      }
435    else
436      array.aset(fillPointer + displacement, element);
437    ++fillPointer;
438  }
439
440  @Override
441  public LispObject VECTOR_PUSH_EXTEND(LispObject element)
442
443  {
444    vectorPushExtend(element);
445    return Fixnum.getInstance(fillPointer - 1);
446  }
447
448  @Override
449  public LispObject VECTOR_PUSH_EXTEND(LispObject element, LispObject extension)
450
451  {
452    int ext = Fixnum.getValue(extension);
453    if (fillPointer < 0)
454      noFillPointer();
455    if (fillPointer >= capacity)
456      {
457        // Need to extend vector.
458        ext = Math.max(ext, capacity + 1);
459        ensureCapacity(capacity + ext);
460      }
461    if (chars != null)
462      {
463        chars[fillPointer] = LispCharacter.getValue(element);
464      }
465    else
466      array.aset(fillPointer + displacement, element);
467    return Fixnum.getInstance(fillPointer++);
468  }
469
470  public final void ensureCapacity(int minCapacity)
471  {
472    if (chars != null)
473      {
474        if (capacity < minCapacity)
475          {
476            char[] newArray = new char[minCapacity];
477            System.arraycopy(chars, 0, newArray, 0, capacity);
478            chars = newArray;
479            capacity = minCapacity;
480          }
481      }
482    else
483      {
484        Debug.assertTrue(array != null);
485        if (capacity < minCapacity ||
486            array.getTotalSize() - displacement < minCapacity)
487          {
488            // Copy array.
489            chars = new char[minCapacity];
490            final int limit =
491              Math.min(capacity, array.getTotalSize() - displacement);
492            if (array instanceof AbstractString)
493              {
494                AbstractString string = (AbstractString) array;
495                for (int i = 0; i < limit; i++)
496                  {
497                    chars[i] = string.charAt(displacement + i);
498                  }
499              }
500            else
501              {
502                for (int i = 0; i < limit; i++)
503                  {
504                    LispCharacter character =
505                      (LispCharacter) array.AREF(displacement + i);
506                    chars[i] = character.value;
507                  }
508              }
509            capacity = minCapacity;
510            array = null;
511            displacement = 0;
512            isDisplaced = false;
513          }
514      }
515  }
516
517  @Override
518  public int sxhash()
519  {
520    int hashCode = randomStringHashBase;
521    final int limit = length();
522    for (int i = 0; i < limit; i++)
523      {
524        hashCode += charAt(i);
525        hashCode += (hashCode << 10);
526        hashCode ^= (hashCode >> 6);
527      }
528    hashCode += (hashCode << 3);
529    hashCode ^= (hashCode >> 11);
530    hashCode += (hashCode << 15);
531    return (hashCode & 0x7fffffff);
532  }
533
534  // For EQUALP hash tables.
535  @Override
536  public int psxhash()
537  {
538    int hashCode = randomStringHashBase;
539    final int limit = length();
540    for (int i = 0; i < limit; i++)
541      {
542        hashCode += Character.toUpperCase(charAt(i));
543        hashCode += (hashCode << 10);
544        hashCode ^= (hashCode >> 6);
545      }
546    hashCode += (hashCode << 3);
547    hashCode ^= (hashCode >> 11);
548    hashCode += (hashCode << 15);
549    return (hashCode & 0x7fffffff);
550  }
551
552  @Override
553  public AbstractVector adjustArray(int newCapacity,
554                                     LispObject initialElement,
555                                     LispObject initialContents)
556
557  {
558    if (initialContents != null)
559      {
560        // "If INITIAL-CONTENTS is supplied, it is treated as for MAKE-
561        // ARRAY. In this case none of the original contents of array
562        // appears in the resulting array."
563        char[] newChars = new char[newCapacity];
564        if (initialContents.listp())
565          {
566            LispObject list = initialContents;
567            for (int i = 0; i < newCapacity; i++)
568              {
569                newChars[i] = LispCharacter.getValue(list.car());
570                list = list.cdr();
571              }
572          }
573        else if (initialContents.vectorp())
574          {
575            for (int i = 0; i < newCapacity; i++)
576              newChars[i] = LispCharacter.getValue(initialContents.elt(i));
577          }
578        else
579          type_error(initialContents, Symbol.SEQUENCE);
580        chars = newChars;
581      }
582    else
583      {
584        if (chars == null)
585          {
586            // Displaced array. Copy existing characters.
587            chars = new char[newCapacity];
588            final int limit = Math.min(capacity, newCapacity);
589            if (array instanceof AbstractString)
590              {
591                AbstractString string = (AbstractString) array;
592                for (int i = 0; i < limit; i++)
593                  {
594                    chars[i] = string.charAt(displacement + i);
595                  }
596              }
597            else
598              {
599                for (int i = 0; i < limit; i++)
600                  {
601                    LispCharacter character =
602                      (LispCharacter) array.AREF(displacement + i);
603                    chars[i] = character.value;
604                  }
605              }
606          }
607        else if (capacity != newCapacity)
608          {
609            char[] newElements = new char[newCapacity];
610            System.arraycopy(chars, 0, newElements, 0,
611                             Math.min(capacity, newCapacity));
612            chars = newElements;
613          }
614        if (initialElement != null && capacity < newCapacity)
615          {
616            // Initialize new elements.
617            final char c = LispCharacter.getValue(initialElement);
618            for (int i = capacity; i < newCapacity; i++)
619              chars[i] = c;
620          }
621      }
622    capacity = newCapacity;
623    array = null;
624    displacement = 0;
625    isDisplaced = false;
626    return this;
627  }
628
629  @Override
630  public AbstractVector adjustArray(int newCapacity,
631                                     AbstractArray displacedTo,
632                                     int displacement)
633
634  {
635    capacity = newCapacity;
636    array = displacedTo;
637    this.displacement = displacement;
638    chars = null;
639    isDisplaced = true;
640    return this;
641  }
642}
Note: See TracBrowser for help on using the repository browser.