

/* Pocket Smalltalk
   Copyright (c) 1998,1999 by Andrew Brault
   http://www.pocketsmalltalk.com
   See LICENSE.TXT for license information */

/* Optimized Collection and Stream primitives.
   Having these increases overall execution speed by
   a -large- factor! */


#include "main.h"


/* Instance variables of Stream */
#define Stream_collection  0
#define Stream_position    1
#define Stream_limit       2


static Object expand_array(Object array, uint16 amount);
static Object expand_byte_array(Object byte_array, uint16 amount);



/* Expands any indexable pointer object; returns a new instance
   of the same class. */
static Object expand_array(Object array, uint16 amount)
{
  uint16 size, named_instvars;
  Object expanded, array_class;

  array_class = OBJECT_CLASS(array);
  named_instvars = FROM_SMALLINT(
       OBJECT_ACCESS(array_class, Behavior_layout)) & 4095;
     
  size = OBJECT_SIZE(array) - named_instvars;  
  expanded = instantiate_indexed(array_class, size + amount);
  MEMCOPY(OBJECT_WORDS(expanded) + named_instvars,
          OBJECT_WORDS(array) + named_instvars, size << 1);
  return expanded;
}


static Object expand_byte_array(Object byte_array, uint16 amount)
{
  Object expanded;
  uint16 size;

  size = OBJECT_BYTE_SIZE(byte_array);
  expanded = instantiate_byte_indexed(OBJECT_CLASS(byte_array),
                                      size + amount);
  MEMCOPY(OBJECT_BYTES(expanded), OBJECT_BYTES(byte_array), size);
  return expanded;
}


/* Array>>#expandedBy:
   Answer a copy of the receiver with extra slots as specified
   by the argument.
   Fails with:
     1 - invalid argument
*/
Object p_array_expanded(Object extra)
{
  int16 amount;

  if(!IS_SMALLINT(extra))
    { FAIL(1); }
  amount = FROM_SMALLINT(extra);
  if(amount < 0)
    { FAIL(1); }
         
  return expand_array(receiver, amount);
}


/* ByteArray>>#expandedBy:, String>>#expandedBy:
   Answer a copy of the receiver with extra slots as specified
   by the argument.  This works for any byte object.
   Fails with:
     1 - invalid argument
*/
Object p_byte_array_expanded(Object extra)
{
  int16 amount;

  if(!IS_SMALLINT(extra))
    { FAIL(1); }
  amount = FROM_SMALLINT(extra);
  if(amount < 0)
    { FAIL(1); }

  return expand_byte_array(receiver, amount);
}


/* StringWriteStream>>#nextPut:
   Append a Character to a StringWriteStream.
   Fails with:
     1 - the argument is not a Character
*/
Object p_strstream_nextput(Object character)
{
#ifdef OPTIMIZED_STRING_STREAMS
  int16 position, limit;
  uint16 expand_amount;
  Object string;

  if(CLASS_OF(character) != Character)
    { FAIL(1); }
  position = FROM_SMALLINT(OBJECT_ACCESS(receiver, Stream_position));
  limit = FROM_SMALLINT(OBJECT_ACCESS(receiver, Stream_limit));
  string = OBJECT_ACCESS(receiver, Stream_collection);
  if(position >= limit) {
    /* Expand the string */
    expand_amount = OBJECT_BYTE_SIZE(string) / 2;
    if(expand_amount < 10) expand_amount = 10;
    string = expand_byte_array(string, expand_amount);
    OBJECT_SET(receiver, Stream_collection, string);
    limit = OBJECT_BYTE_SIZE(string);
    OBJECT_SET(receiver, Stream_limit, TO_SMALLINT(limit));
  }
  OBJECT_BYTE_ACCESS(string, position) =
        FROM_CHARACTER(character);
  position++;
  OBJECT_SET(receiver, Stream_position,
             TO_SMALLINT(position));
  return character;
#else
  FAIL(1);
#endif
}


/* StringWriteStream>>#nextPutAll:
   ByteArrayWriteStream>>#nextPutAll:
   Append a String to a StringWriteStream.
   Fails with:
     1 - the argument is not a String
*/
Object p_strstream_nextputall(Object string)
{
#ifdef OPTIMIZED_STRING_STREAMS
  int16 position, limit;
  uint16 string_len, expand_amount;
  Object buffer;

  if(CLASS_OF(string) != OBJECT_CLASS(receiver))
    { FAIL(1); }
  position = FROM_SMALLINT(OBJECT_ACCESS(receiver, Stream_position));
  limit = FROM_SMALLINT(OBJECT_ACCESS(receiver, Stream_limit));
  buffer = OBJECT_ACCESS(receiver, Stream_collection);
  string_len = OBJECT_BYTE_SIZE(string);
  if(position + string_len >= limit) {  /* >= is conservative */
    expand_amount = OBJECT_BYTE_SIZE(buffer) / 2;
    if(expand_amount < 10) expand_amount = 10;
    if(expand_amount < string_len) expand_amount = string_len;
    buffer = expand_byte_array(buffer, expand_amount);
    OBJECT_SET(receiver, Stream_collection, buffer);
    limit = OBJECT_BYTE_SIZE(buffer);
    OBJECT_SET(receiver, Stream_limit, TO_SMALLINT(limit));
  }
  MEMCOPY(OBJECT_BYTES(buffer) + position,
          OBJECT_BYTES(string), string_len);
  position += string_len;
  OBJECT_SET(receiver, Stream_position, TO_SMALLINT(position));
  return string;
#else
  FAIL(1);
#endif
}


/* Array>>#copyFrom:to:
   Fails with:
     1 - either argument is not a SmallInteger
     2 - arguments out of bounds
     3 - result collection is empty (can retry in superclass)
*/
Object p_array_copyfrom_to(Object start_index, Object stop_index)
{
  int16 start, stop, array_size, sublength;
  Object subarray;

  array_size = OBJECT_SIZE(receiver);
  if(!(IS_SMALLINT(start_index) && IS_SMALLINT(stop_index)))
    { FAIL(1); }
  start = FROM_SMALLINT(start_index);
  stop = FROM_SMALLINT(stop_index);
  if(start < 1 || stop < 1 ||
     start > array_size || stop > array_size)
    { FAIL(2); }
  if(start > stop)
    { FAIL(3); }
  start--;
  stop--;
  sublength = stop - start + 1;
  subarray = instantiate_indexed(OBJECT_CLASS(receiver), sublength);
  MEMCOPY(OBJECT_WORDS(subarray),
          OBJECT_WORDS(receiver) + start,
          sublength << 1);
  return subarray;
}


/* ByteArray>>#copyFrom:to:
   Works for Strings and other byte-indexed objects too
   Fails with:
     1 - either argument is not a SmallInteger
     2 - arguments out of bounds
     3 - result collection is empty (can retry in superclass)
*/
Object p_bytearray_copyfrom_to(Object start_index, Object stop_index)
{
  int16 start, stop, array_size, sublength;
  Object subarray;

  array_size = OBJECT_BYTE_SIZE(receiver);
  if(!(IS_SMALLINT(start_index) && IS_SMALLINT(stop_index)))
    { FAIL(1); }
  start = FROM_SMALLINT(start_index);
  stop = FROM_SMALLINT(stop_index);
  if(start < 1 || stop < 1 ||
     start > array_size || stop > array_size)
    { FAIL(2); }
  if(start > stop)
    { FAIL(3); }
  start--;
  stop--;
  sublength = stop - start + 1;
  subarray = instantiate_byte_indexed(OBJECT_CLASS(receiver), sublength);
  MEMCOPY(OBJECT_BYTES(subarray),
          OBJECT_BYTES(receiver) + start,
          sublength);
  return subarray;
}


