Package erjang

Source Code of erjang.EBinList$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 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 EBinList extends ECons {

  /**
   *
   */
  private static final int INITIAL_BUFFER_SIZE = 10;
  final byte[] data;
  final int off;
  final int len;
  final EObject tail;

  // synchronized access
  /** True if <code>data</code> is shared by other instances of EBinList.
   * In that case, we need to copy the data array if we need to grow. */
  private boolean shared;

  /** create list as sublist of data given */
  private EBinList(byte[] data, int off, int len, EObject tail, boolean shared) {
   
    assert len != 0;
   
    this.data = data;
    this.off = off;
    this.len = len;
    this.tail = tail;
    this.shared = shared;

    assert(tail!=null);
    if (len < 1 || off + len > data.length)
      throw ERT.badarg();
  }

  @Override
  public int hashCode() {
    ESeq seq;
    if ((seq=testSeq()) != null) return seq.hashCode();

    //TODO: use phash() rather than this brittle nonsense:
    int h = 0;
    for (int i=off; i<off+len; i++) {
      h = 31*h + data[i];
    }
    return h + tail.hashCode();
  }

  /** create a list with [value|tail], where value is a smallint 0..255 */
  public EBinList(byte value, EObject tail) {
    this.data = new byte[INITIAL_BUFFER_SIZE];
    this.off = INITIAL_BUFFER_SIZE-1;
    this.len = 1;
    this.tail = tail;
    this.shared = false;

    data[off] = value;
  }
 
  public ECons testNonEmptyList() {
    if (len == 0)
      return tail.testNonEmptyList();
    return this;
  }
 
  public ESeq testSeq() {
    if (tail.testSeq() != null) {
      return this.seq();
    } else {
      return null;
    }
  }


  /**
   * @param header
   * @param tail
   */
  public EBinList(byte[] header, EObject tail) {
    this(header, 0, header.length, tail, true);
  }

  /**
   * @param header
   * @param tail2
   */
  public EBinList(ByteBuffer buf, EObject tail) {
    this(buf.array(), buf.arrayOffset()+buf.position(), buf.remaining(), tail, true);
  }

  @Override
  public ECons cons(EObject h) {
    ESmall sm;
    ESeq seqTail;
    if ((sm = h.testSmall()) != null && sm.value >= 0 && sm.value < 256) {

      byte[] res_data = data;
      int res_off = off;
      int res_len = len + 1;

      synchronized (this) {
        if (shared || off == 0) {
          res_data = new byte[len * 2 + 1];
          System.arraycopy(data, off, res_data, len + 1, len);
          res_off = len + 1;
        } else {
          shared = true;
        }
      }

      res_data[--res_off] = (byte) sm.value;
      return new EBinList(res_data, res_off, res_len, tail, res_data==data);

    } else if ((seqTail=this.testSeq()) != null) {
      return new EList(h, seqTail);
    } else {
      return new EPair(h, this);
    }
  }

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

    synchronized (this) {
      shared = true;
    }
    return new EBinList(data, off+n, len-n, tail, true);
  }

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

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

    return new EBinList(data, off + 1, len - 1, tail, true);
  }

  // TODO: Remote this method all together
  @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;
    }
   
    byte[] out_bin = new byte[len + st.length()];
    System.arraycopy(this.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 EBinList.this.testNonEmptyList();
    }

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

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

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

    @Override
    public void encode(EOutputStream eos) {
      EBinList.this.encode(eos);
    }
  }
 
  private boolean all_printable() {
    byte val;
    for (int i = 0; i < len; i++) {
      val = 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 = 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 = 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(data, off, len);
    tail.visitIOList(out);
  }
 
  @Override
  public boolean collectIOList(List<ByteBuffer> out) {
    out.add(ByteBuffer.wrap(data, off, len));
    return tail.collectIOList(out);
  }

  @Override
  public ESeq collectCharList(CharCollector out, ESeq rest)
    throws CharCollector.CollectingException,
    CharCollector.InvalidElementException,
    IOException
  {
    if (rest != ERT.NIL) {
      return rest.cons(this);
    }
   
    try {
      rest = out.addBinary(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) {
    eos.write_list_head(len);
    for (int i = 0; i < len; i++) {
      eos.write_int(data[off+i]);
    }
    eos.write_any(tail);
  }
 
  public static EBinList fromString(String c, EObject tail) {
    byte[] data = new byte[c.length()];
    for (int i = 0; i < data.length; i++) {
      data[i] = (byte) c.charAt(i);
    }
    return new EBinList(data, tail);
  }
 
  private static final Type EBINLIST_TYPE = Type.getType(EBinList.class);
  private static final Type STRING_TYPE = Type.getType(String.class);
  private static final String EOBJECT_DESC = Type.getDescriptor(EObject.class);

  @Override
  public Type emit_const(MethodVisitor fa) {

    Type type = EBINLIST_TYPE;

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

    return type;
  }

}
TOP

Related Classes of erjang.EBinList$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.