| /* |
| * 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)); |
| } |
| } |
| } |