Package erjang

Source Code of erjang.EStringList$Seq

/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

package erjang;

import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

/**
* Special cons cell optimizing for [byte, byte, byte | Tail]. 
* An <code>EBinList</code> has a tail, and an array of bytes for the
* values it contains.  It is optimized for the case of cons'ing a
* byte onto it; it works like a java StringBuilder, but its contents
* array grows backwards; so the bytes contained in it are stored in
* the last bytes of the <code>data</code> array variable.
*/
public class EStringList extends ESeq {

  static class PrependableBytes {
    AtomicInteger start_pos;
    byte[] data;

    PrependableBytes(int size) {
      this.data = new byte[size];
      this.start_pos = new AtomicInteger(data.length);
    }

    PrependableBytes(int size, byte v) {
      this.data = new byte[size];
      data[size-1] = v;
      this.start_pos = new AtomicInteger(data.length-1);
    }

    PrependableBytes(byte[] org, int extra) {
      this(org, 0, org.length, extra);
    }

    PrependableBytes(byte[] org, int off, int len, int extra) {
      this(org.length + extra);
      System.arraycopy(org,off, data,data.length-len, len);
    }

    PrependableBytes prepend(int old_start, byte b) {
      assert (old_start > 0);
      int new_start = old_start-1;
      if (start_pos.compareAndSet(old_start, new_start)) {
        data[new_start] = b;
        return this;
      } else return null;
    }

    boolean prepend2(int old_start, byte b) {
      assert (old_start > 0);
      int new_start = old_start-1;
      if (start_pos.compareAndSet(old_start, new_start)) {
        data[new_start] = b;
        return true;
      } else return false;
    }

    PrependableBytes prepend(int old_start, byte[] org, int off, int len) {
      assert (old_start > 0);
      int new_start = old_start-len;
      if (start_pos.compareAndSet(old_start, new_start)) {
        System.arraycopy(org,off, data,new_start, len);
        return this;
      } else return null;
    }
  }

  /**
   *
   */
  private static final int INITIAL_BUFFER_SIZE = 10;
  final PrependableBytes bytes;
  final int off;
  final int len;
  final ESeq tail;

  private EStringList(PrependableBytes bytes, int off, int len, ESeq tail) {
    assert len>0;
    this.bytes = bytes;
    this.off = off;
    this.len = len;
    this.tail = tail;
  }


  /** create a list with [value|tail], where value is a smallint 0..255 */
  public EStringList(byte value, ESeq tail) {
    this(new PrependableBytes(INITIAL_BUFFER_SIZE, value),
       INITIAL_BUFFER_SIZE-1, 1, tail);
  }

  public EStringList(byte[] header, ESeq tail) {
    this(new PrependableBytes(header, INITIAL_BUFFER_SIZE),
       INITIAL_BUFFER_SIZE, header.length, tail);
  }

  public ECons testNonEmptyList() {
    if (len == 0)
      return tail.testNonEmptyList();
    return this;
  }

  public ESeq testSeq() {
    return this;
  }

  @Override
  public ESeq cons(EObject h) {
    ESmall sm = h.testSmall();
    if (sm != null) {
      int value = sm.value;
      if ((value & ~0xff) == 0) { // Fits in a byte
        if (off==0) return new EStringList((byte)value, this);

        if (bytes.prepend2(this.off, (byte)value)) {
          return new EStringList(bytes, off-1, len+1, tail);
        } else {
          return new EStringList((byte)value, this);
        }
      }
    }
    return new EList(h, this);
  }

  public EObject drop(int n) {
    if (n > len) throw new IllegalArgumentException();
    if (n == len) return tail;

    return new EStringList(bytes, off + n, len - n, tail);
  }

  @Override
  public ESmall head() {
    return ESmall.little[(bytes.data[off] & 0xff)];
  }

  @Override
  public ESeq tail() {
    if (len == 1)
      return tail;

    return new EStringList(bytes, off + 1, len - 1, tail);
  }

  // TODO: Remove this method altogether
  @Override
  public boolean isNil() {
    assert len != 0;
    return false;
  }

  @Override
  public ENil testNil() {
    if (isNil())
      return ERT.NIL;
    return null;
  }

  public EString testString() {
    EString st = tail.testString();
    if (st == null) {
      return null;
    }
    //TODO: handle stringlist chain more efficiently?
    byte[] out_bin = new byte[len + st.length()];
    System.arraycopy(this.bytes.data, this.off, out_bin, 0, this.len);
    System.arraycopy(st.data, st.off, out_bin, len, st.length());
    return new EString(out_bin, 0);
  }

  private ESeq seq() { return new Seq(); }

   /**
   * Helper class that looks at this EBinList as a Seq.
   */
  private class Seq extends ESeq {
    @Override
    public ECons testNonEmptyList() {
      return EStringList.this.testNonEmptyList();
    }

    @Override
    public ESeq cons(EObject h) {
      return EStringList.this.cons(h);
    }

    @Override
    public ESeq tail() {
      return EStringList.this.tail();
    }

    @Override
    public EObject head() {
      return EStringList.this.head();
    }

    @Override
    public void encode(EOutputStream eos) {
      EStringList.this.encode(eos);
    }
  }
 
  private boolean all_printable() {
    byte val;
    for (int i = 0; i < len; i++) {
      val = bytes.data[off+i];
      if (val < ' ' || val >= 127) {
        return false;
      }
    }
    return true;
  }

  @Override
  public String toString() {
   
    //
    // Can this be printed as "..."
    //
    if (tail.isNil() && all_printable()) {
      StringBuilder sb = new StringBuilder("\"");
      for (int i = 0; i < len; i++) {
        byte val = bytes.data[off+i];
        sb.append((char)val);
      }
      sb.append('"');
      return sb.toString();
    }
   
    //
    // Otherwise, print it as [.., .., |..]
    //
    StringBuilder sb = new StringBuilder("[");
   
    int max = Math.min(len, 40);
    for (int i = 0; i < max; i++) {
      if (i != 0) { sb.append(","); }
      byte val = bytes.data[off+i];
      if (val > ' ' && val < 127) {
        sb.append('$');
        sb.append((char)val);
      } else {
        sb.append(val & 0xFF);
      }
    }
   
    if (max!=len) {
      sb.append("...");
    }
   
    if (!tail.isNil()) {
      sb.append('|');
      sb.append(tail);
    }
   
    sb.append("]");
    return sb.toString();
  }
 
  @Override
  public void visitIOList(EIOListVisitor out) throws ErlangError {
    out.visit(bytes.data, off, len); //?
    tail.visitIOList(out);
  }
 
  @Override
  public boolean collectIOList(List<ByteBuffer> out) {
    out.add(ByteBuffer.wrap(bytes.data, off, len)); //?
    return tail.collectIOList(out);
  }

  @Override
  public ESeq collectCharList(CharCollector out, ESeq rest)
    throws CharCollector.CollectingException,
    CharCollector.InvalidElementException,
    IOException
  {
    try {
      rest = out.addBinary(bytes.data, off, len, rest);
    } catch (CharCollector.PartialDecodingException e) {
      throw new CharCollector.CollectingException(drop(e.inputPos - off));
    }

    if (tail.testNumber() != null) {
      // Only nil and binaries are allowed as tail
      // TODO: Fail sooner?
      throw new CharCollector.InvalidElementException();
    } else {
      return tail.collectCharList(out, rest);
    }
  }

  @Override
  public void encode(EOutputStream eos) {
    if (tail.isNil()) {
      eos.write_string(bytes.data, off, len);
    } else { //TODO: Check whether tail is EString or EStringList
      eos.write_list_head(len);
      for (int i = 0; i < len; i++) {
        eos.write_int(bytes.data[off+i]);
      }
      eos.write_any(tail);
    }
  }
 
  public static EStringList fromString(String c, ESeq tail) {
    byte[] data = new byte[c.length()];
    for (int i = 0; i < data.length; i++) {
      data[i] = (byte) c.charAt(i);
    }
    return new EStringList(data, tail);
  }
 
  private static final Type ESTRINGLIST_TYPE = Type.getType(EStringList.class);
  private static final Type STRING_TYPE = Type.getType(String.class);
  private static final String ESEQ_DESC = Type.getDescriptor(ESeq.class);

  @Override
  public Type emit_const(MethodVisitor fa) {

    Type type = ESTRINGLIST_TYPE;

    char[] ch = new char[len];
    for (int i = 0; i < len; i++) {
      ch[i] = (char)(0xff & (int)bytes.data[off+i]);
    }
   
    fa.visitLdcInsn(new String(ch));
    tail.emit_const(fa);
    fa.visitMethodInsn(Opcodes.INVOKESTATIC, type.getInternalName(),
        "fromString", "(" + STRING_TYPE.getDescriptor() + ESEQ_DESC + ")"
            + type.getDescriptor());

    return type;
  }

}
TOP

Related Classes of erjang.EStringList$Seq

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.