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

org.luaj.vm.LString Maven / Gradle / Ivy

There is a newer version: 1.0.5
Show newest version
/*******************************************************************************
* Copyright (c) 2007 LuaJ. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;



/**
 * A String implementation for Lua using bytes instead of chars.
 * 
 * This should have the following advantages:
 * 
 * (1) We can use strings as byte buffers, as Lua does, and therefore avoid
 * questions about how to adapt Lua APIs that use strings with binary data.
 * 
 * (2) Half the memory usage when strings are primarily ASCII
 * 
 * 
 * TODO: Decide if/when to copy the bytes to a new array to ensure memory does
 * not "leak" in the form of unused portions of byte arrays. Currently, for
 * efficiency, new LStrings and substrings never create copies.
 */
public class LString extends LValue {
	
	public byte[] m_bytes;
	public int m_offset;
	public final int m_length;
	public final int m_hash;
	
	private static LTable s_stringMT;
	
	public static final LString[] LTYPENAMES;
	
	static {
		int n = Lua.TYPE_NAMES.length;
		LTYPENAMES = new LString[n];
		for ( int i=0; i=0||i>=j)?
					b:
				(b<-32||i+1>=j)? 
					(((b&0x3f) << 6) 
						| (m_bytes[i++]&0x3f)):
					(((b&0xf) << 12) 
						| ((m_bytes[i++]&0x3f)<<6)
						|  (m_bytes[i++]&0x3f))
				);
		}
		return new String(c,0,n);
	}
	
	/**
	 * Construct a string from the given byte array.
	 * 
	 * new LString(b) is identical to new LString(b, 0, b.length)
	 */
	public LString(byte[] bytes) {
		this( bytes, 0, bytes.length );
	}
	
	/**
	 * Construct a string from the given byte array and range. For efficiency,
	 * the byte array is not copied. Lua strings are immutable so the bytes must
	 * not be modified after the string is constructed.
	 */
	public LString(byte[] bytes, int off, int len) {
		if ( off < 0 || len < 0 || off+len > bytes.length )
			throw new IndexOutOfBoundsException();
		this.m_bytes = bytes;
		this.m_offset = off;
		this.m_length = len;
		this.m_hash = hashBytes( bytes, off, len );
	}
	
	public static LString newStringCopy(LString src) {
		return newStringCopy( src.m_bytes, src.m_offset, src.m_length );
	}
	
	public static LString newStringCopy(byte[] buf, int off, int len) {
		byte[] b = new byte[len];
		System.arraycopy( buf, off, b, 0, len );
		return new LString( b, 0, len );
	}
	
	public static LString newStringNoCopy(byte[] buf, int off, int len) {
		return new LString( buf, off, len );
	}
	
	/**
	 * Count the number of bytes required to encode the string as UTF-8.
	 */
	public static int lengthAsUtf8(String string) {
		int n = string.length();
		int b = n;
		char c;
		for ( int i=0; i= 0x80 ) {
				++b;
				if ( c >= 0x800 )
					++b;
			}
		}
		return b;
	}
	
	/**
	 * Encode the given Java string as UTF-8 bytes, writing the result to bytes
	 * starting at offset. The string should be measured first with lengthAsUtf8
	 * to make sure the given byte array is large enough.
	 */
	public static void encodeToUtf8(String string, byte[] bytes, final int startOffset) {
		final int n = string.length();
		for ( int i=0, j=startOffset; i>6)  & 0x1f));
				bytes[j++] = (byte) (0x80 | ( c      & 0x3f));				
			} else {
				bytes[j++] = (byte) (0xE0 | ((c>>12) & 0x0f));
				bytes[j++] = (byte) (0x80 | ((c>>6)  & 0x3f));
				bytes[j++] = (byte) (0x80 | ( c      & 0x3f));				
			}
		}
	}
	
	public boolean isString() {
		return true;
	}
	
	public boolean equals(Object o) {
		if ( this == o )
			return true;
		if ( o != null && o instanceof LString ) {
			LString s = (LString) o;
			if ( m_hash == s.m_hash && m_length == s.m_length ) {
				if ( m_bytes == s.m_bytes && m_offset == s.m_offset )
						return true;
				if ( equals( m_bytes, m_offset, s.m_bytes, s.m_offset, m_length ) ) {
					if ( m_bytes.length < s.m_bytes.length )  {
						s.m_bytes = m_bytes;
						s.m_offset = m_offset;
					} else {
						m_bytes = s.m_bytes;
						m_offset = s.m_offset;
					}
					return true;
				}
			}
		}
		return false;
	}
	
	public int compareTo( LString o ) {
		final byte[] a = this.m_bytes;
		final byte[] b = o.m_bytes;
		int i = this.m_offset;
		int j = o.m_offset;
		final int imax = i + m_length;
		final int jmax = j + o.m_length;
		
		if ( a == b && i == j && imax == jmax )
			return 0;
		
		while ( i < imax && j < jmax ) {
			if ( a[i] != b[j] ) {
				return ( ( (int)a[i] ) & 0x0FF ) - ( ( (int)b[j] ) & 0x0FF );
			}
			i++;
			j++;
		}
		
		return m_length - o.m_length;
	}
	
	public int hashCode() {
		return m_hash;
	}

	public int length() {
		return m_length;
	}
	
	public LString substring( int beginIndex, int endIndex ) {
		return new LString( m_bytes, m_offset + beginIndex, endIndex - beginIndex );
	}
	
	public int charAt( int index ) {
		if ( index < 0 || index >= m_length )
			throw new IndexOutOfBoundsException();
		return luaByte( index );
	}
	
	/** Java version of strpbrk, which is a terribly named C function. */
	public int indexOfAny( LString accept ) {
		final int ilimit = m_offset + m_length;
		final int jlimit = accept.m_offset + accept.m_length;
		for ( int i = m_offset; i < ilimit; ++i ) {
			for ( int j = accept.m_offset; j < jlimit; ++j ) {
				if ( m_bytes[i] == accept.m_bytes[j] ) {
					return i - m_offset;
				}
			}
		}
		return -1;
	}
	
	public int indexOf( byte b, int start ) {
		for ( int i = m_offset + start; i < m_length; ++i ) {
			if ( m_bytes[i] == b )
				return i;
		}
		return -1;
	}
	
	public int indexOf( LString s, int start ) {
		final int slen = s.length();
		final int limit = m_offset + m_length - slen;
		for ( int i = m_offset + start; i <= limit; ++i ) {
			if ( equals( m_bytes, i, s.m_bytes, s.m_offset, slen ) ) {
				return i;
			}
		}
		return -1;
	}
	
	public int lastIndexOf( LString s ) {
		final int slen = s.length();
		final int limit = m_offset + m_length - slen;
		for ( int i = limit; i >= m_offset; --i ) {
			if ( equals( m_bytes, i, s.m_bytes, s.m_offset, slen ) ) {
				return i;
			}
		}
		return -1;
	}
	
	public static LString valueOf( double d ) {
		return new LString( String.valueOf( d ) );
	}
	
	public static LString valueOf( int x ) {
		return new LString( String.valueOf( x ) );
	}
	
	public static LString valueOf(String s) {
		return new LString( s );
	}
	
	/**
	 * Write the specified substring of this string to the given output stream.
	 */
	public void write( OutputStream os, int offset, int len ) throws IOException {
		if ( offset < 0 || len < 0 )
			throw new IndexOutOfBoundsException();
		if ( offset + len > m_length )
			throw new IndexOutOfBoundsException();

		os.write( m_bytes, m_offset+offset, len );
	}

	public void write(OutputStream os) throws IOException {
		write(os, 0, m_length);
	}
	
	/**
	 * Copy the bytes of the string into the given byte array.
	 */
	public void copyInto( int strOffset, byte[] bytes, int arrayOffset, int len ) {
		System.arraycopy( m_bytes, m_offset+strOffset, bytes, arrayOffset, len );
	}
	
	/**
	 * Produce an InputStream instance from which the bytes of this LString can be read.
	 * Underlying byte array is not copied.
	 */
	public ByteArrayInputStream toInputStream() {
		// Well, this is really something.
		// Javadoc for java versions 1.3 and earlier states that if reset() is
		// called on a ByteArrayInputStream constructed with the 3-argument
		// constructor, then bytes 0 .. offset will be returned by the next
		// calls to read(). In JDK 1.4, the behavior improved, so that the
		// initial mark is set to the initial offset. We still need to
		// override ByteArrayInputStream here just in case we run on a
		// JVM with the older behavior.
		return new ByteArrayInputStream( m_bytes, m_offset, m_length ) {
			public synchronized void reset() {
				pos = Math.max( m_offset, mark );
			}
		};
	}
	
	public boolean luaBinCmpUnknown(int opcode, LValue lhs) {
		return lhs.luaBinCmpString(opcode, this);
	}

	public boolean luaBinCmpString(int opcode, LString rhs) {
		switch ( opcode ) {
		case Lua.OP_EQ: return equals(rhs);
		case Lua.OP_LT: return compareTo(rhs) < 0;
		case Lua.OP_LE: return compareTo(rhs) <= 0;
		default: break;
		}
		LuaState.vmerror( "bad cmp opcode" );
		return false;
	}
	
	public LValue luaBinOpDouble( int opcode, double m_value ) {
		return luaToNumber().luaBinOpDouble( opcode, m_value );
	}
	
	public LValue luaBinOpInteger( int opcode, int m_value ) {
		return luaToNumber().luaBinOpInteger( opcode, m_value );
	}
	
	public LValue luaBinOpUnknown( int opcode, LValue lhs ) {
		return luaToNumber().luaBinOpUnknown( opcode, lhs );
	}
	
	public LValue luaUnaryMinus() {
		return luaToNumber().luaUnaryMinus();
	}
	
	public LValue luaToNumber() {
		return luaToNumber( 10 );
	}
	
	public LValue luaToNumber( int base ) {
		if ( base >= 2 && base <= 36 ) {
			String str = toJavaString().trim();
			if ( ( base == 10 || base == 16 ) && ( str.startsWith("0x") || str.startsWith("0X") ) ) {
				base = 16;
				str = str.substring(2);
			}
			try {
				long x = Long.parseLong(str, base);
				if (x < Integer.MIN_VALUE || x > Integer.MAX_VALUE)
					return new LDouble((double) x);
				else
					return LInteger.valueOf((int) x);
			} catch ( NumberFormatException nfe ) {
				if ( base == 10 ) {
					try {
						return LDouble.numberOf( Double.parseDouble( str ) );
					} catch ( NumberFormatException nfe2 ) {
					}
				}
			}
		}
		
		return LNil.NIL;
	}
	
	public LString luaAsString() {
		return this;
	}
	
	/** Built-in opcode LEN, for Strings and Tables */
	public int luaLength() {
		return m_length;
	}

	public int luaGetType() {
		return Lua.LUA_TSTRING;
	}
	
	public LTable luaGetMetatable() {
		synchronized ( LString.class ) {
			return s_stringMT;
		}
	}
	
	/**
	 * Get the metatable for all string values. Creates the table if it does not
	 * exist yet, and sets its __index entry to point to itself.
	 * 
	 * @return metatable that will be used for all strings
	 */
	public static synchronized LTable getMetatable() {
		if ( s_stringMT == null ) {
			s_stringMT = new LTable();
			s_stringMT.put( TM_INDEX, s_stringMT );
		}
		return s_stringMT;
	}
	
	public static boolean equals( LString a, int i, LString b, int j, int n ) {
		return equals( a.m_bytes, a.m_offset + i, b.m_bytes, b.m_offset + j, n );
	}
	
	public static boolean equals( byte[] a, int i, byte[] b, int j, int n ) {
		if ( a.length < i + n || b.length < j + n )
			return false;
		while ( --n>=0 ) 
			if ( a[i++]!=b[j++] )
				return false;
		return true;
	}
	
	private static int hashBytes( byte[] bytes, int offset, int length ) {
		// Compute the hash of the given bytes.
		// This code comes right out of Lua 5.1.2 (translated from C to Java)
		int h = length;  /* seed */
		int step = (length>>5)+1;  /* if string is too long, don't hash all its chars */
		for (int l1=length; l1>=step; l1-=step)  /* compute hash */
		    h = h ^ ((h<<5)+(h>>2)+(((int) bytes[offset+l1-1] ) & 0x0FF ));
		return h;
	}
	
	public int luaByte(int index) {
		return m_bytes[m_offset + index] & 0x0FF;
	}

	public void luaConcatTo(ByteArrayOutputStream baos) {
		baos.write( m_bytes, m_offset, m_length );
	}
	
	/** Returns true if this is or can be made into a number */
	public boolean isNumber() {
		return ! this.luaToNumber().isNil();
	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy