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

com.crankuptheamps.client.fields.StringField Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2024 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties.  This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights.  This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////

package com.crankuptheamps.client.fields;

import java.io.UnsupportedEncodingException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;

import com.crankuptheamps.client.CommandId;

/**
 * Field data for a {@link com.crankuptheamps.client.Message} which consists of
 * a string.
 */
public class StringField extends Field
{
    protected static final String LATIN1       = "ISO-8859-1";
    protected static final byte   LATIN1_ZERO  = 48;

    private ByteBuffer value =
            ByteBuffer.allocate(getConversionBufInitialSize());
    private byte[]     bytes = value.array();

    // These are used only for quick conversion without collecting garbage
    private CharBuffer charValue  =
            CharBuffer.allocate(getConversionBufInitialSize());
    private char[]     charBytes  = charValue.array();
    
    /**
     * Returns the initial default size for the byte and char conversion buffers
     * used by this instance. Defaults to 256 for this class.
     * 
     * @return Returns a default size of 256.
     */
    protected int getConversionBufInitialSize() { return 256; }

    /**
     * Initializes a new instance of the StringField class with the
     * specified buffer, position, and length.
     * @param buffer    The byte array buffer containing the string
     *                  data.
     * @param position  The starting position of the string within
     *                  the buffer.
     * @param length    The length of the string.
     */
    protected StringField(byte[] buffer, int position, int length)
    {
        super(buffer, position, length);
    }

    /**
     * Default constructor.
     */
    public StringField()
    {
    }

    /**
     * Initializes a new instance of the StringField class with the specified
     * string value.
     * @param value The string value to initialize the StringField with.
     */
    public StringField(String value)
    {
        super(value);
    }

    /**
     * Returns a string representation of the Field value, where the bytes are interpreted by using the specified 
     * decoder. 
     *
     * @param decoder The charset decoder used to interpret bytes as characters.
     * @return a newly-constructed String representing the value of this field
     */
    public String getValue(CharsetDecoder decoder)
    {
        if(this.buffer != null)
        {
            return new String(this.buffer,this.position,this.length,decoder.charset());
        }
        return null;
    }
    
    /**
     * Writes the byte value of this instance into the specified byte buffer, if the
     * value is not null.
     *
     * @param v The byte buffer to write in to.
     * @return Return true if this instance is not null, otherwise false.
     */
    public boolean getValue(ByteBuffer v)
    {
        if(this.buffer != null)
        {
            v.put(this.buffer,this.position,this.length);
            return true;
        }
        return false;
    }

    /**
     * Writes the byte value of this instance into the specified command ID buffer
     * object, if the value is not null.
     *
     * @param v The command ID object to write in to.
     * @return Return true if this instance is not null, otherwise false.
     */
    public boolean getValue(CommandId v)
    {
        if(this.buffer != null)
        {
            v.set(this.buffer,this.position,this.length);
            return true;
        }
        return false;
    }

    /**
     * Sets the value of this instance with the specified region of a byte buffer.
     * Note: the bytes are not copied from the buffer. The specified buffer region
     * is referenced by this instance.
     *
     * @param v      The byte buffer.
     * @param offset The starting position of the value in the byte buffer.
     * @param length The length from the starting position of the value in the byte
     *               buffer.
     */
    public void setValue(byte[] v,int offset,int length)
    {
        this.buffer   = v;
        this.position = offset;
        this.length   = length;
    }

    /**
     * Sets the value of this instance with the specified buffer from the command ID object.
     * Note: the bytes are not copied from the buffer. The specified buffer region is
     * referenced by this instance.
     *
     * @param v The command ID object whose value is used to set this instance.
     */
    public void setValue(CommandId v)
    {
        this.buffer   = v.id;
        this.position = 0;
        if(v.id == null)
        {
            this.length   = 0;
        } else {
            this.length = v.id.length;
        }

    }

    /**
     * Sets the value of this instance from the specified string. The characters of the string
     * are interpreted using the specified encoder. This method makes its own copy of the bytes.
     *
     * @param v       The string used as the value.
     * @param encoder The encoder used to interpret the string's characters as
     *                bytes.
     */
    public void setValue(String v, CharsetEncoder encoder)
    {
        if(v == null)
        {
            reset();
            return;
        }
        int vLength = v.length();
        // Check if charbuffer conversion space needs expansion
        int reqSize = charValue.capacity();
        if(vLength > reqSize)
        {
            // Must resize
            while(vLength > reqSize) reqSize *= 2;
            charValue = CharBuffer.allocate(reqSize);
            charBytes = charValue.array();
        }
        // Check if buffer storage space needs expansion
        reqSize = value.capacity();
        float maxBytesPerChar = encoder.maxBytesPerChar();
        if(vLength*maxBytesPerChar > reqSize)
        {
            // Must resize
            while (vLength * maxBytesPerChar > reqSize)
                reqSize *= 2;
            value = ByteBuffer.allocate(reqSize);
            bytes = value.array();
        }
        // Copy to the charbuffer
        charValue.clear();
        v.getChars(0, vLength, charBytes, 0); // Turns out this method is faster than doing a put
        charValue.limit(vLength);

        // Encode to the byte buffer
        value.clear();
        encoder.reset();
        try
        {
            CoderResult result = encoder.encode(charValue,value,true);
            if(result.isError()) result.throwException();
        }
        catch(Exception ex)
        {
            throw new RuntimeException(ex);
        }
        set(bytes, 0, value.position());

        // getBytes is faster than using a CharBuffer, however, there's a bunch of garbage
        // collected in using this method, so we avoid it. (about a 7% bump in performance,
        // but under heavy load it creates some GC issues.)
        // try { this.buffer = v.getBytes(LATIN1); } catch(UnsupportedEncodingException e) {;}
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy