blob: ba2cd144b2563c105ab7e562f3575097e8fea183 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* 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 com.google.gwt.typedarrays.client;
import com.google.gwt.typedarrays.shared.ArrayBuffer;
import com.google.gwt.typedarrays.shared.DataView;
import com.google.gwt.typedarrays.shared.Float32Array;
import com.google.gwt.typedarrays.shared.Float64Array;
import com.google.gwt.typedarrays.shared.Int16Array;
import com.google.gwt.typedarrays.shared.Int32Array;
import com.google.gwt.typedarrays.shared.Uint16Array;
import com.google.gwt.typedarrays.shared.Uint32Array;
/**
* JS native implementation of {@link DataView} for platforms with typed array
* support but missing DataView (ie, Firefox).
*/
public class DataViewNativeEmul implements DataView {
private static final boolean nativeLittleEndian;
static {
nativeLittleEndian = isNativeLittleEndian();
}
/**
* @param buffer
* @param byteOffset
* @param byteLength
* @return a {@link DataView} instance
*/
public static DataView create(ArrayBuffer buffer, int byteOffset,
int byteLength) {
return new DataViewNativeEmul(buffer, byteOffset, byteLength);
}
/**
* @return true if this platform's typed arrays are little-endian
*/
private static native boolean isNativeLittleEndian() /*-{
var i8 = new Int8Array(4);
i8[0] = 1;
i8[1] = 2;
i8[2] = 3;
i8[3] = 4;
var i32 = new Int32Array(i8.buffer);
// TODO(jat): do we need to check for other endianness beside BE/LE?
return i32[0] == 0x04030201;
}-*/;
protected final ArrayBuffer buffer;
protected final int bufferByteOffset;
protected final int byteLength;
/**
* A temporary buffer used for reversing bytes.
*/
protected final Uint8ArrayNative tempBuffer;
/**
* A view of the underlying buffer as bytes.
*/
protected final Uint8ArrayNative uint8Array;
protected DataViewNativeEmul(ArrayBuffer buffer, int byteOffset, int byteLength) {
this.buffer = buffer;
this.bufferByteOffset = byteOffset;
this.byteLength = byteLength;
tempBuffer = Uint8ArrayNative.create(ArrayBufferNative.create(8), 0, 8);
uint8Array = Uint8ArrayNative.create(buffer, byteOffset, byteLength);
}
@Override
public ArrayBuffer buffer() {
return buffer;
}
@Override
public int byteLength() {
return byteLength;
}
@Override
public int byteOffset() {
return bufferByteOffset;
}
@Override
public float getFloat32(int byteOffset) {
return getFloat32(byteOffset, false);
}
@Override
public float getFloat32(int byteOffset, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int len = Float32Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
reverseBytes(uint8Array, ofs, len, tempBuffer, 0);
buf = tempBuffer.buffer();
ofs = 0;
}
return Float32ArrayNative.create(buf, ofs, 1).get(0);
}
@Override
public double getFloat64(int byteOffset) {
return getFloat64(byteOffset, false);
}
@Override
public double getFloat64(int byteOffset, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int len = Float64Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
reverseBytes(uint8Array, ofs, len, tempBuffer, 0);
buf = tempBuffer.buffer();
ofs = 0;
}
return Float64ArrayNative.create(buf, ofs, 1).get(0);
}
@Override
public short getInt16(int byteOffset) {
return getInt16(byteOffset, false);
}
@Override
public short getInt16(int byteOffset, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int len = Int16Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
reverseBytes(uint8Array, ofs, len, tempBuffer, 0);
buf = tempBuffer.buffer();
ofs = 0;
}
return Int16ArrayNative.create(buf, ofs, 1).get(0);
}
@Override
public int getInt32(int byteOffset) {
return getInt32(byteOffset, false);
}
@Override
public int getInt32(int byteOffset, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int len = Int32Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
reverseBytes(uint8Array, ofs, len, tempBuffer, 0);
buf = tempBuffer.buffer();
ofs = 0;
}
return Int32ArrayNative.create(buf, ofs, 1).get(0);
}
@Override
public byte getInt8(int byteOffset) {
return Int8ArrayNative.create(buffer, byteOffset, 1).get(0);
}
@Override
public int getUint16(int byteOffset) {
return getUint16(byteOffset, false);
}
@Override
public int getUint16(int byteOffset, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int len = Uint16Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
reverseBytes(uint8Array, ofs, len, tempBuffer, 0);
buf = tempBuffer.buffer();
ofs = 0;
}
return Uint16ArrayNative.create(buf, ofs, 1).get(0);
}
@Override
public long getUint32(int byteOffset) {
return getUint32(byteOffset, false);
}
@Override
public long getUint32(int byteOffset, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int len = Uint32Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
reverseBytes(uint8Array, ofs, len, tempBuffer, 0);
buf = tempBuffer.buffer();
ofs = 0;
}
return Uint32ArrayNative.create(buf, ofs, 1).get(0);
}
@Override
public double getUint32AsDouble(int byteOffset) {
return getUint32(byteOffset, false);
}
@Override
public double getUint32AsDouble(int byteOffset, boolean littleEndian) {
return getUint32(byteOffset, littleEndian);
}
@Override
public short getUint8(int byteOffset) {
return Uint8ArrayNative.create(buffer, byteOffset, 1).get(0);
}
@Override
public void setFloat32(int byteOffset, float value) {
setFloat32(byteOffset, value, false);
}
@Override
public void setFloat32(int byteOffset, float value, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int finalOfs = ofs;
int len = Float32Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
buf = tempBuffer.buffer();
ofs = 0;
}
Float32ArrayNative.create(buf, ofs, 1).set(0, value);
if (littleEndian != nativeLittleEndian) {
reverseBytes(tempBuffer, 0, len, uint8Array, finalOfs);
}
}
@Override
public void setFloat64(int byteOffset, double value) {
setFloat64(byteOffset, value, false);
}
@Override
public void setFloat64(int byteOffset, double value, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int finalOfs = ofs;
int len = Float64Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
buf = tempBuffer.buffer();
ofs = 0;
}
Float64ArrayNative.create(buf, ofs, 1).set(0, value);
if (littleEndian != nativeLittleEndian) {
reverseBytes(tempBuffer, 0, len, uint8Array, finalOfs);
}
}
@Override
public void setInt16(int byteOffset, int value) {
setInt16(byteOffset, value, false);
}
@Override
public void setInt16(int byteOffset, int value, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int finalOfs = ofs;
int len = Int16Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
buf = tempBuffer.buffer();
ofs = 0;
}
Int16ArrayNative.create(buf, ofs, 1).set(0, value);
if (littleEndian != nativeLittleEndian) {
reverseBytes(tempBuffer, 0, len, uint8Array, finalOfs);
}
}
@Override
public void setInt32(int byteOffset, int value) {
setInt32(byteOffset, value, false);
}
@Override
public void setInt32(int byteOffset, int value, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int finalOfs = ofs;
int len = Int32Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
buf = tempBuffer.buffer();
ofs = 0;
}
Int32ArrayNative.create(buf, ofs, 1).set(0, value);
if (littleEndian != nativeLittleEndian) {
reverseBytes(tempBuffer, 0, len, uint8Array, finalOfs);
}
}
@Override
public void setInt8(int byteOffset, int value) {
Int8ArrayNative.create(buffer, byteOffset, 1).set(0, value);
}
@Override
public void setUint16(int byteOffset, int value) {
setUint16(byteOffset, value, false);
}
@Override
public void setUint16(int byteOffset, int value, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int finalOfs = ofs;
int len = Uint16Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
buf = tempBuffer.buffer();
ofs = 0;
}
Uint16ArrayNative.create(buf, ofs, 1).set(0, value);
if (littleEndian != nativeLittleEndian) {
reverseBytes(tempBuffer, 0, len, uint8Array, finalOfs);
}
}
@Override
public void setUint32(int byteOffset, long value) {
setUint32(byteOffset, value, false);
}
@Override
public void setUint32(int byteOffset, long value, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int finalOfs = ofs;
int len = Uint32Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
buf = tempBuffer.buffer();
ofs = 0;
}
Uint32ArrayNative.create(buf, ofs, 1).set(0, value);
if (littleEndian != nativeLittleEndian) {
reverseBytes(tempBuffer, 0, len, uint8Array, finalOfs);
}
}
@Override
public void setUint32FromDouble(int byteOffset, double value) {
setUint32FromDouble(byteOffset, value, false);
}
@Override
public void setUint32FromDouble(int byteOffset, double value, boolean littleEndian) {
ArrayBuffer buf = buffer;
int ofs = bufferByteOffset + byteOffset;
int finalOfs = ofs;
int len = Uint32Array.BYTES_PER_ELEMENT;
if (littleEndian != nativeLittleEndian) {
buf = tempBuffer.buffer();
ofs = 0;
}
Uint32ArrayNative.create(buf, ofs, 1).set(0, value);
if (littleEndian != nativeLittleEndian) {
reverseBytes(tempBuffer, 0, len, uint8Array, finalOfs);
}
}
@Override
public void setUint8(int byteOffset, int value) {
Uint8ArrayNative.create(buffer, byteOffset, 1).set(0, value);
}
/**
* Copy bytes from the underlying buffer to a temporary buffer, reversing them
* in the process.
*
* @param src
* @param srcOfs offset into {@code buffer}
* @param len number of bytes to copy
* @param dest
* @param destOfs
*/
protected final void reverseBytes(Uint8ArrayNative src, int srcOfs, int len,
Uint8ArrayNative dest, int destOfs) {
for (int i = 0; i < len; ++i) {
dest.set(i + destOfs, src.get(srcOfs + len - 1 - i));
}
}
}