All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.quercus.lib.UnserializeReader Maven / Gradle / Ivy

There is a newer version: 4.0.66
Show newest version
/*
 * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.quercus.lib;

import com.caucho.quercus.env.*;
import com.caucho.quercus.function.AbstractFunction;
import com.caucho.util.L10N;
import com.caucho.util.LruCache;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Logger;

public final class UnserializeReader {
  private static final L10N L = new L10N(UnserializeReader.class);
  private static final Logger log
    = Logger.getLogger(UnserializeReader.class.getName());

  private static final LruCache _keyCache
    = new LruCache(4096);

  private final char []_buffer;
  private final int _length;

  private int _index;
  private StringKey _key = new StringKey();

  private ArrayList _valueList
    = new ArrayList();

  private ArrayList _referenceList
    = new ArrayList();

  private boolean _useReference;

  public UnserializeReader(StringValue s)
    throws IOException
  {
    _buffer = s.toCharArray();
    _length = _buffer.length;

    if (s.indexOf("R:") >= 0
        || s.indexOf("r:") >= 0)
      initReferenceList();
  }

  public UnserializeReader(String s)
     throws IOException
  {
    _buffer = s.toCharArray();
    _length = _buffer.length;

    if (s.indexOf("R:") >= 0
        || s.indexOf("r:") >= 0)
      initReferenceList();
  }

  protected boolean useReference()
  {
    return _useReference;
  }

  public Value unserialize(Env env)
    throws IOException
  {
    int ch = read();

    switch (ch) {
    case 'b':
      {
        expect(':');
        long v = readInt();
        expect(';');

        Value value = v == 0 ? BooleanValue.FALSE : BooleanValue.TRUE;

        if (_useReference)
          value = createReference(value);

        return value;
      }

    case 's':
    case 'S':
    case 'u':
    case 'U':
      {
        expect(':');
        int len = (int) readInt();
        expect(':');
        expect('"');

        if (! isValidString(len)) {
          env.notice(L.l("expected string length of {0}", len));
          return BooleanValue.FALSE;
        }

        Value value;

        if (ch == 's' || ch == 'S') {
          value = readStringValue(env, len);
        }
        else {
          value = readUnicodeValue(env, len);
        }

        expect('"');
        expect(';');

        if (_useReference)
          value = createReference(value);

        return value;
      }

    case 'i':
      {
        expect(':');

        long l = readInt();

        expect(';');

        Value value = LongValue.create(l);

        if (_useReference)
          value = createReference(value);

        return value;
      }

    case 'd':
      {
        expect(':');

        StringBuilder sb = new StringBuilder();
        for (ch = read(); ch >= 0 && ch != ';'; ch = read()) {
          sb.append((char) ch);
        }

        if (ch != ';') {
          throw new IOException(L.l("expected ';'"));
        }

        Value value = new DoubleValue(Double.parseDouble(sb.toString()));

        if (_useReference)
          value = createReference(value);

        return value;
      }

    case 'a':
      {
        expect(':');
        int len = (int) readInt();
        expect(':');
        expect('{');

        Value array = new ArrayValueImpl(len);

        if (_useReference) {
          array = createReference(array);
        }

        for (int i = 0; i < len; i++) {
          Value key = unserializeKey(env);
          Value value = unserialize(env);

          array.put(key, value);
        }

        expect('}');

        return array;
      }

    case 'O':
      {
        expect(':');
        int len = (int) readInt();
        expect(':');
        expect('"');

        if (! isValidString(len)) {
          return BooleanValue.FALSE;
        }

        String className = readString(len);

        expect('"');
        expect(':');
        int count = (int) readInt();
        expect(':');
        expect('{');

        QuercusClass qClass = env.findClass(className);
        Value obj;

        if (qClass != null) {
          obj = qClass.createObject(env);
        }
        else {
          log.fine(L.l("{0} is an undefined class in unserialize",
                   className));

          obj = env.createIncompleteObject(className);
        }

        Value ref = null;

        if (_useReference) {
          ref = createReference(obj);
        }

        for (int i = 0; i < count; i++) {
          StringValue key = unserializeKey(env).toStringValue();

          FieldVisibility visibility = FieldVisibility.PUBLIC;

          if (key.length() == 0) {
            throw new IOException(L.l("field key is empty for class {0})",
                                      className));
          }

          if (key.charAt(0) == 0) {
            if (key.length() > 3
                && key.charAt(1) == '*'
                && key.charAt(2) == 0) {
              visibility = FieldVisibility.PROTECTED;

              //key = key.substring(3);
            }
            else if (key.length() > 4) {
              int end = key.indexOf("\u0000", 1);

              if (end < 0) {
                throw new IOException(L.l("end of field visibility modifier is not valid: 0x{0} ({1}, {2})",
                                          Integer.toHexString(key.charAt(2)), className, key));
              }

              StringValue declaringClass = key.substring(1, end);
              StringValue fieldName = key.substring(end + 1);

              visibility = FieldVisibility.PRIVATE;
            }
            else {
              throw new IOException(L.l("field visibility modifier is not valid: 0x{0} ({1}, {2})",
                                        Integer.toHexString(key.charAt(1)), className, key));
            }
          }

          Value value = unserialize(env);

          obj.initIncompleteField(env, key, value, visibility);
        }

        expect('}');

        if (ref != null)
          return ref;
        else
          return obj;
      }

    case 'C':
      {
        expect(':');
        int len = (int) readInt();
        expect(':');
        expect('"');

        if (! isValidString(len)) {
          return BooleanValue.FALSE;
        }

        String className = readString(len);

        expect('"');
        expect(':');
        int count = (int) readInt();
        expect(':');
        expect('{');

        QuercusClass qClass = env.findClass(className);

        if (qClass == null) {
          log.fine(L.l("{0} is an undefined class in unserialize",
                   className));

          return BooleanValue.FALSE;
        }

        AbstractFunction fun = qClass.getUnserialize();

        if (fun == null) {
          log.fine(L.l("{0} does not implement unserialize()",
                       className));

          return BooleanValue.FALSE;
        }

        if (! isValidString(count)) {
          return BooleanValue.FALSE;
        }

        String data = readString(count);
        StringValue dataV = env.createString(data);

        Value obj = qClass.createObject(env, false);
        fun.callMethod(env, qClass, obj, dataV);

        expect('}');

        return obj;
      }

    case 'N':
      {
        expect(';');

        Value value = NullValue.NULL;

        if (_useReference)
          value = createReference(value);

        return value;
      }
    case 'R':
      {
        expect(':');

        int index = (int) readInt();

        expect(';');

        if (index - 1 >= _valueList.size()) {
          throw new IOException(L.l("reference out of range: {0}, size {1}, index {2}",
                                    index - 1, _valueList.size(), _index));
          //return BooleanValue.FALSE;
        }

        Value ref = _valueList.get(index - 1);

        return ref;
      }
    case 'r':
      {
        expect(':');

        int index = (int) readInt();

        expect(';');

        if (index - 1 >= _valueList.size()) {
          throw new IOException(L.l("reference out of range: {0}, size {1}, index {2}",
                                    index - 1, _valueList.size(), _index));
          //return BooleanValue.FALSE;
        }

        Value value = _valueList.get(index - 1).copy();

        if (_useReference)
          value = createReference(value);

        return value;

      }

    default:
      throw new IOException(L.l("option not recognized '{0}' (0x{1}) at index {2} ({3})",
                                String.valueOf((char) ch),
                                Integer.toHexString(ch),
                                _index,
                                String.valueOf(_buffer)));

      //return BooleanValue.FALSE;
    }
  }

  public Value createReference(Value value)
  {
    if (_referenceList.get(_valueList.size()) == Boolean.FALSE) {

      _valueList.add(value);
      return value;
    }
    else {
      Var var = new Var(value);

      _valueList.add(var);
      return var;
    }
  }

  private void initReferenceList()
    throws IOException
  {
    populateReferenceList();

    _index = 0;
  }

  private void populateReferenceList()
    throws IOException
  {
    int ch = read();

    switch (ch) {
      case 'b':
      {
        _referenceList.add(Boolean.FALSE);

        expect(':');
        for (ch = read(); ch >= 0 && ch != ';'; ch = read()) {
        }

        return;
      }

      case 's':
      case 'S':
      case 'u':
      case 'U':
      {
        _referenceList.add(Boolean.FALSE);

        expect(':');
        int len = (int) readInt();
        expect(':');
        expect('"');

        _index += len;

        expect('"');
        expect(';');

        return;
      }

      case 'i':
      {
        _referenceList.add(Boolean.FALSE);

        expect(':');

        for (ch = read(); ch >= 0 && ch != ';'; ch = read()) {
        }

        return;
      }

      case 'd':
      {
        _referenceList.add(Boolean.FALSE);

        expect(':');

        for (ch = read(); ch >= 0 && ch != ';'; ch = read()) {
        }

        return;
      }

      case 'a':
      {
        _referenceList.add(Boolean.FALSE);

        expect(':');
        int len = (int) readInt();
        expect(':');
        expect('{');

        for (int i = 0; i < len; i++) {
          switch (read()) {
          case 's':
          case 'S':
            {
              expect(':');
              int keyLen = (int) readInt();
              expect(':');
              expect('"');

              _index += keyLen;

              expect('"');
              expect(';');

              break;

            }

          case 'i':
            {
              expect(':');

              for (ch = read(); ch >= 0 && ch != ';'; ch = read()) {
              }

              break;
            }
          }

          populateReferenceList();
        }

        expect('}');

        return;
      }

      case 'O':
      {
        _referenceList.add(Boolean.FALSE);

        expect(':');
        int len = (int) readInt();
        expect(':');
        expect('"');

        _index += len;

        expect('"');
        expect(':');
        int count = (int) readInt();
        expect(':');
        expect('{');

        for (int i = 0; i < count; i++) {
          switch (read()) {
            case 's':
            case 'S':
            {
              expect(':');
              int keyLen = (int) readInt();
              expect(':');
              expect('"');

              _index += keyLen;

              expect('"');
              expect(';');

              break;
            }

            case 'i':
            {
              expect(':');

              for (ch = read(); ch >= 0 && ch != ';'; ch = read()) {
              }

              break;
            }
          }

          populateReferenceList();
        }

        expect('}');

        return;
      }

      case 'N':
      {
        _referenceList.add(Boolean.FALSE);

        expect(';');

        return;
      }

      case 'R':
      {
        _referenceList.add(Boolean.FALSE);

        _useReference = true;

        expect(':');

        int value = (int) readInt();

        expect(';');

        _referenceList.set(value - 1, Boolean.TRUE);

        return;
      }

      case 'r':
      {
        _referenceList.add(Boolean.FALSE);

        _useReference = true;

        expect(':');

        int value = (int) readInt();

        expect(';');

        return;
      }
    }
  }

  public Value unserializeKey(Env env)
    throws IOException
  {
    int ch = read();

    switch (ch) {
      case 's':
      case 'S':
      case 'u':
      case 'U':
      {
        expect(':');
        int len = (int) readInt();
        expect(':');
        expect('"');

        StringValue v;

        if (len < 32) {
          _key.init(_buffer, _index, len);

          v = _keyCache.get(_key);

          if (v != null) {
            _index += len;
          }
          else {
            StringKey key = new StringKey(_buffer, _index, len);

            if (ch == 's' || ch == 'S') {
              v = readStringValue(env, len);
            }
            else {
              v = readUnicodeValue(env, len);
            }

            _keyCache.put(key, v);
          }
        }
        else {
          v = readStringValue(env, len);
        }

        expect('"');
        expect(';');

        return v;
      }

      case 'i':
      {
        expect(':');

        long value = readInt();

        expect(';');

        return LongValue.create(value);
      }

    default:
      return BooleanValue.FALSE;
    }
  }

  private String unserializeString()
    throws IOException
  {
    int ch = read();

    if (ch != 's' && ch != 'S') {
      throw new IOException(L.l("expected 's' at '{1}' (0x{2})",
                                String.valueOf((char) ch),
                                Integer.toHexString(ch)));
    }

    expect(':');
    int len = (int) readInt();
    expect(':');
    expect('"');

    String s = readString(len);

    expect('"');
    expect(';');

    return s;
  }

  public final void expect(int expectCh)
    throws IOException
  {
    if (_length <= _index)
      throw new IOException(L.l("expected '{0}' at end of string",
                                String.valueOf((char) expectCh)));

    int ch = _buffer[_index++];

    if (ch != expectCh) {
      String context = String.valueOf((char) ch);

      if (_index - 2 >= 0)
        context = _buffer[_index - 2] + context;
      if (_index < _buffer.length)
        context += _buffer[_index];

      throw new IOException(
          L.l("expected '{0}' at '{1}' (0x{2}) (context '{3}', index {4})",
                                String.valueOf((char) expectCh),
                                String.valueOf((char) ch),
                                Integer.toHexString(ch),
                                context,
                                _index));
    }
  }

  public final long readInt()
  {
    int ch = read();

    long sign = 1;
    long value = 0;

    if (ch == '-') {
      sign = -1;
      ch = read();
    }
    else if (ch == '+') {
      ch = read();
    }

    for (; '0' <= ch && ch <= '9'; ch = read()) {
      value = 10 * value + ch - '0';
    }

    unread();

    return sign * value;
  }

  public final boolean isValidString(int len)
  {
    if (_index + len >= _buffer.length)
      return false;

    return true;
  }

  public final String readString(int len)
  {
    String s = new String(_buffer, _index, len);

    _index += len;

    return s;
  }

  public final StringValue readStringValue(Env env, int len)
  {
    StringValue s = env.createString(_buffer, _index, len);

    _index += len;

    return s;
  }

  public final StringValue readUnicodeValue(Env env, int len)
  {
    StringValue s = new UnicodeBuilderValue(_buffer, _index, len);

    _index += len;

    return s;
  }

  public final int read()
  {
    if (_index < _length)
      return _buffer[_index++];
    else
      return -1;
  }

  public final int read(char []buffer, int offset, int length)
  {
    System.arraycopy(_buffer, _index, buffer, offset, length);

    _index += length;

    return length;
  }

  public final void unread()
  {
    _index--;
  }

  public final static class StringKey
  {
    char []_buffer;
    int _offset;
    int _length;

    StringKey()
    {
    }

    StringKey(char []buffer, int offset, int length)
    {
      _buffer = new char[length];
      System.arraycopy(buffer, offset, _buffer, 0, length);
      _offset = 0;
      _length = length;
    }

    void init(char []buffer, int offset, int length)
    {
      _buffer = buffer;
      _offset = offset;
      _length = length;
    }

    public int hashCode()
    {
      char []buffer = _buffer;
      int offset = _offset;
      int end = offset + _length;
      int hash = 17;

      for (; offset < end; offset++)
        hash = 65521 * hash + buffer[offset];

      return hash;
    }

    public boolean equals(Object o)
    {
      if (! (o instanceof StringKey))
        return false;

      StringKey key = (StringKey) o;

      int length = _length;

      if (length != key._length)
        return false;

      char []aBuf = _buffer;
      char []bBuf = key._buffer;

      int aOffset = _offset;
      int bOffset = key._offset;

      int aEnd = aOffset + length;

      while (aOffset < aEnd) {
        if (aBuf[aOffset++] != bBuf[bOffset++])
          return false;
      }

      return true;
    }
  }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy