I am using boost crc32 to simluate java version:
long HashUtils::CalcCRC32(const std::wstring& filePath)
{
HANDLE hFile = CreateFile(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
std::vector<byte> buff;
DWORD dwSize = GetFileSize(hFile, NULL);
buff.resize(dwSize);
boost::crc_32_type crc32;
DWORD bytesRead = 0;
if (!ReadFile(hFile, buff.data(), buff.size(), &bytesRead, 0) || bytesRead != dwSize) {
CloseHandle(hFile);
return 0;
}
CloseHandle(hFile);
crc32.process_block(buff.data(), buff.data() + buff.size());
return crc32.checksum();
}
The java version
private static long calcCRC32(byte[] raw, int offset, int len) throws IOException {
CRC32 crc32 = new CRC32();
crc32.update(raw, offset, len);
return crc32.getValue();
}
The problem is java version return long, which is 64bit, while the c++/boost version return long which is 32bit. Will the crc32 result of the java version overflow 32bit?
The java CRC32 class only returns 32 bit CRCs. Therefore the int datatype is large enough to hold all the information. You can rewrite your function as this:
private static int calcCRC32(byte[] raw, int offset, int len) throws IOException
{
CRC32 crc32 = new CRC32();
crc32.update(raw, offset, len);
return (int)crc32.getValue();
}
Now you should have similar results from the C++ and java version.
The reason that crc.getValue() returns a long instead of an int is quite simple by the way: CRC32 implements the Checksum interface. This interface has been specified to return long for the getValue() method. This was done to allow for checksum implementations that return more than 32 useful bits of information.
Related
I use this solution to call the JNA method from .dll/.so library:
Creating C++ Structures via JNA
And in Windows, this code works perfect, but in Linux, I receive truncated double values, so from the Pointer data
in Windows, I receive this value:
40.7
from these bytes in Pointer:
[64, 68, 89, -103, -103, -103, -103, -102]
But in Linux I have this instead:
40.0
[64, 68, 0, 0, 0, 0, 0, 0]
.so/.dll was compiled from the same source and from the Python(by using "ctypes") I obtain correct values in Linux from the same .so
I already tried to write/read byte[] to/from Pointer - nothing happened - in Windows, all OK, but in the Linux, doubles are truncated.
This is C++ Structure and Method, which it returns inside of .dll/.so:
struct emxArray_real_T
{
double *data;
int *size;
int allocatedSize;
int numDimensions;
boolean_T canFreeData;
};
emxArray_real_T *emxCreate_real_T(int rows, int cols)
{
emxArray_real_T *emx;
***
return emx;
}
And this code in Java:
#Structure.FieldOrder({"data", "size", "allocatedSize", "numDimensions", "canFreeData"})
public class emxArray_real_T extends Structure {
public Pointer data;
public Pointer size;
public int allocatedSize = 1;
public int numDimensions = 1;
public boolean canFreeData = false;
***
public double[] getData() {
if (data == null) {
return new double[0];
}
return data.getDoubleArray(0, allocatedSize);
}
public void setData(double[] data) {
if (data.length != allocatedSize) {
throw new IllegalArgumentException("Data must have a length of " + allocatedSize + " but was "
+ data.length);
}
this.data.write(0, data, 0, data.length);
}
***
}
UPD: Obtaining bytes from Pointer data:
public byte[] getDataByte() {
final int times = Double.SIZE / Byte.SIZE;
if (data == null) {
return new byte[0];
}
return data.getByteArray(0, allocatedSize * times);
}
UPD2: I receive this error when I try to write the double like this -0.0000847 in the Pointer data in Linux(so it seems in Linux .so somehow use C type 'int' to the double representation):
Expected a value representable in the C type 'int'. Found inf instead. Error in ballshaftpasses (line 61)
UPD3: double[] <-> byte[] conversions(trying to solve Linux issue):
Changes in getData() setData:
public double[] getData() {
final int times = Double.SIZE / Byte.SIZE;
if (data == null) {
return new double[0];
}
return ByteDoubleConverterUtils.toDoubleArray(data.getByteArray(0, allocatedSize * times));
}
public void setData(double[] data) {
final byte[] bytes = ByteDoubleConverterUtils.toByteArray(data);
this.data.write(0, bytes, 0, bytes.length);
}
Utility class:
public class ByteDoubleConverterUtils {
public static byte[] toByteArray(double[] doubleArray){
int times = Double.SIZE / Byte.SIZE;
byte[] bytes = new byte[doubleArray.length * times];
for(int i=0;i<doubleArray.length;i++){
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes, i * times, times);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
byteBuffer.putDouble(doubleArray[i]);
}
return bytes;
}
public static double[] toDoubleArray(byte[] byteArray){
int times = Double.SIZE / Byte.SIZE;
double[] doubles = new double[byteArray.length / times];
for(int i=0;i<doubles.length;i++){
final ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray, i * times, times);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
doubles[i] = byteBuffer.getDouble();
}
return doubles;
}
}
UPD4:
C++ Function Example
This is C++ function example to reproduce problems in Linux:
void emx_copy(const emxArray_real_T *in, emxArray_real_T *res)
{
int i0;
int loop_ub;
i0 = res->size[0] * res->size[1];
res->size[0] = in->size[0];
res->size[1] = in->size[1];
emxEnsureCapacity_real_T(res, i0);
loop_ub = in->size[0] * in->size[1];
for (i0 = 0; i0 < loop_ub; i0++) {
res->data[i0] = in->data[i0] * in->data[i0];
}
}
Java JNA Interface:
public interface EmxJna extends Library {
FcnOpjJna INSTANCE = (EmxJna)Native.load(Native.load(Platform.isWindows() ? "emx.dll" : "emx.so", EmxJna.class);
emxArray_real_T emxCreate_real_T(int rows, int cols);
void emx_copy(emxArray_real_T in, emxArray_real_T res);
}
Java test Method to call lib and set values 40.7 and -0.0000847 in Pointer data:
private void emxTest() {
final emxArray_real_T input = FcnOpjJna.INSTANCE.emxCreate_real_T(128, 32);
final double[] inputData = input.getData();
inputData[0] = 40.7d;
inputData[1] = -0.0000847d;
input.setData(inputData);
final emxArray_real_T output = FcnOpjJna.INSTANCE.emxCreate_real_T(128, 32);
FcnOpjJna.INSTANCE.emx_copy(input, output);
}
Suppose I have the following method:
My question is: What changes can I make to the method below would allow me to implement this same function except while reading type Long (from an inputstream or should I use a DataInputStream instead)? (The call is.readLong(); will be used somewhere in the method probably) Essentially I want a timeout on input.readLong().
public static int readInputStreamWithTimeout(InputStream is, byte[] b, int timeoutMillis) throws IOException
{
int bufferOffset = 0;
long maxTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (System.currentTimeMillis() < maxTimeMillis && bufferOffset < b.length)
{
int readLength = java.lang.Math.min(is.available(),b.length-bufferOffset);
// can alternatively use bufferedReader, guarded by isReady():
int readResult = is.read(b, bufferOffset, readLength);
if (readResult == -1) break;
bufferOffset += readResult;
}
return bufferOffset;
}
Method summary: This method is designed to read from an Inputstream called is until a certain amount of time has passed (then it stops reading, exits the method and returns bufferOffset.) Essentially there is a timeout in the form of int timeoutMillis that will put a limit on reading the inputstream.
Method calling:
byte[] inputData = new byte[1024];
int readCount = readInputStreamWithTimeout(System.in, inputData, 6000); // 6 second timeout
Notice: The line that says int readResult = is.read(b, bufferOffset, readLength); is using read() function which doesn't accept the type Long.
Essentially this would be how it might be executed:
Long inputData = 0;
inputData = readInputStreamWithTimeout(System.in, inputData, 6000); // 6 second timeout
And something like this?
public static Long readInputStreamWithTimeout(InputStream is, byte[] b, int timeoutMillis) throws IOException
{
Long something = 0;
int bufferOffset = 0;
long maxTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (System.currentTimeMillis() < maxTimeMillis && bufferOffset < b.length)
{
int readLength = java.lang.Math.min(is.available(),b.length-bufferOffset);
something = is.readLong();
if (readResult == -1) break;
bufferOffset += readResult;
}
return something;
}
I am looking at memory allocations of an application that writes to sockets, and I see that a lot of allocations are done in SocketOutputStream.socketWrite in InetAddress:
Is there a way do get rid of these allocations?
I am profiling on Windows using Java 1.8.0_40.
Below is the source code of SocketOutputStream from jdk 1.8, I do not see the code that can allocate these objects
private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
int len) throws IOException;
/**
* Writes to the socket with appropriate locking of the
* FileDescriptor.
* #param b the data to be written
* #param off the start offset in the data
* #param len the number of bytes that are written
* #exception IOException If an I/O error has occurred.
*/
private void socketWrite(byte b[], int off, int len) throws IOException {
if (len <= 0 || off < 0 || off + len > b.length) {
if (len == 0) {
return;
}
throw new ArrayIndexOutOfBoundsException();
}
FileDescriptor fd = impl.acquireFD();
try {
socketWrite0(fd, b, off, len);
} catch (SocketException se) {
if (se instanceof sun.net.ConnectionResetException) {
impl.setConnectionResetPending();
se = new SocketException("Connection reset");
}
if (impl.isClosedOrPending()) {
throw new SocketException("Socket closed");
} else {
throw se;
}
} finally {
impl.releaseFD();
}
}
I'm new to Java and a I need to read a binary file and display its contents converted as integers. The file has this structure:
{client#, position 1, size 32 |
category, position 33, size 10 |
type, position 43, size 10 |
creditlimit, position 53, size 20}
I need just a guide on what classes to use and a convertion example, a little snipet will be appreciated.
I assume that position 1 actually is 0; the first byte.
Also it seems the file format is of fixed size records, probably with ASCII in the bytes.
To check the data, I start with taking the fields in Strings. Converting them to long/int could loose information on the actual content.
The following uses a sequential binary file. Faster would be a memory mapped file, but this is acceptable and short.
Hold the client data:
class Client {
String clientno;
String category;
String type;
String position;
String creditlimit;
#Override
public String toString() {
return String.format("Client# %s, categ %s, type %s, pos %s, creditlimit %s%n",
clientno, category, type, position, creditlimit);
}
}
Read the file:
// Field sizes:
final int CLIENT_NO = 32;
final int CATEGORY = 10;
final int TYPE = 10;
final int CREDIT_LIMIT = 20;
final int RECORD_SIZE = CLIENT_NO + CATEGORY + TYPE + CREDIT_LIMIT;
byte[] record = new byte[RECORD_SIZE];
try (BufferedInputStream in = new BufferedInputStream(
new FileInputStream(file))) {
for (;;) {
int nread = in.read(record);
if (nread < RECORD_SIZE) {
break;
}
Client client = new Client();
int offset = 0;
int offset2 = offset + CLIENT_NO;
client.clientno = recordField(record, offset, offset2 - offset);
offset = offset2;
int offset2 = offset + CATEGORY;
client.category = recordField(record, offset, offset2 - offset);
offset = offset2;
int offset2 = offset + TYPE;
client.type = recordField(record, offset, offset2 - offset);
offset = offset2;
int offset2 = offset + CREDITLIMIT;
client.creditlimit = recordField(record, offset, offset2 - offset);
System.out.println(client);
}
} // Closes in.
with a field extraction:
private static String recordField(byte[] record, int offset, int length) {
String field = new String(record, offset, length, StandardCharsets.ISO_8859_1);
// Use ASCII NUL as string terminator:
int pos = field.indexOf('\u0000');
if (pos != -1) {
field = field.substring(0, pos);
}
return field.trim(); // Trim also spaces for fixed fields.
}
If i understand your question correct, you should use the NIO Package.
With the asIntBuffer() from the byteBuffer class, you can get an IntBuffer view of a ByteBuffer. And by calling get(int[] dst) you could convert it to integers.
The initial ByteBuffer is available by using file channels.
If you work with binary data, may be JBBP will be comfortable way for you, to parse and print the data structure with the framework is very easy (if I understood the task correctly and you work with byte fields), the example parsing whole input stream and then print parsed data to console
#Bin class Record {byte [] client; byte [] category; byte [] type; byte [] creditlimit;};
#Bin class Records {Record [] records;};
Records parsed = JBBPParser.prepare("records [_] {byte [32] client; byte [10] category; byte [10] type; byte [20] creditlimit;}").parse(THE_INPUT_STREAM).mapTo(Records.class);
System.out.println(new JBBPTextWriter().Bin(parsed).toString());
I am trying to share data stored in a couchbase memcached bucket between Java and .Net.
I was able to read a string set in Java in .Net but whenever I try to read a string set in .Net in Java the result is Null.
So is it possible to exchange data between .Net and Java in memcache buckets in a couchbase server.
Thanks for the reply, I figured it out.
The reason .NET is able to read strings set in Java is because the enyimMemcached library interprets the cached item as a string if it does not recognize the flag.
So in order to be able to read the strings in Java, I simply created my own custom transcoder by extending the SpyObject and set it in away such as it ignores the flag. I then pass the custom transcoder with my get call like this,
_obj = GetMemcachedClient().get(key, new StringTranscoder())
My StringTranscoder class looks like this,
/**
* Copyright (C) 2006-2009 Dustin Sallings
*
* 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 DEALING
* IN THE SOFTWARE.
*/
package cachingjavatestapp;
import java.io.IOException;
import net.spy.memcached.CachedData;
import net.spy.memcached.compat.SpyObject;
import net.spy.memcached.transcoders.Transcoder;
import net.spy.memcached.transcoders.TranscoderUtils;
/**
* Transcoder that serializes and unserializes longs.
*/
public final class StringTranscoder extends SpyObject implements
Transcoder<String> {
private static final int FLAGS = 0;
public boolean asyncDecode(CachedData d) {
return false;
}
public CachedData encode(java.lang.String l) {
try{
return new CachedData(FLAGS, l.getBytes("UTF-8"), getMaxSize());
}
catch (Exception e){
return null;
}
}
public String decode(CachedData d) {
try{
return new String(d.getData(), "UTF-8");
}catch(Exception e){
return null;
}
}
public int getMaxSize() {
return CachedData.MAX_SIZE;
}
}
In order to be able to exchange data between .NET and Java. I simply used the json.net library and the gson library to serialize objects and pass the json strings to memcached where it gets picked up as a string then deserialized using the json libraries.
Regards,
Yes, it is possible. In the case of the Java client, it has a built in "transcoder" that will handle converting a java.lang.String to bytes with appropriate encoding (UTF-8 I think? I'd have to check). The .NET side would be able to read this data back in.
Where things get sticky is how each client library stores a string. In memcached protocol, the recommended but not required way to do this is with the flags. The challenge is, each client library does flags differently, something the Couchbase client library developers are looking to resolve into a common set of flags.
This means, for now, to normalize how the data is stored between the two client libraries, you may have to set up the client library a particular way. For example, Java has customizable transcoders and you can extend one of the existing transcoders to read/write your strings with the flags that the .NET client library is using.
Let me know what client libraries you're using, and I'll update this with an example.
I know this is a rather old question, but I had the same problem and thought I'd share my current solution. The following transcoder for EnyimMemcached more closely matches the way types are serialized/flagged in spymemcached. Obviously this is NOT going to work if you attempt to serialize objects between .Net and Java; but it will let you work with more than just strings.
https://github.com/mikeleedev/EnyimMemcached/blob/master/Enyim.Caching/Memcached/Transcoders/SpymemcachedTranscoder.cs
public class SpymemcachedTranscoder : ITranscoder
{
#region Private Members
// General flags
private const uint SERIALIZED = 1; //00000000 00000001
private const uint COMPRESSED = 2; //00000000 00000010 <-- TODO - add support for compression
private const uint NOFLAG = 0; //00000000 00000000
// Special flags for specially handled types.
private const uint SPECIAL_MASK = 0xff00; //11111111 00000000
private const uint SPECIAL_BOOLEAN = (1 << 8); //00000001 00000000
private const uint SPECIAL_INT = (2 << 8); //00000010 00000000
private const uint SPECIAL_LONG = (3 << 8); //00000011 00000000
private const uint SPECIAL_DATE = (4 << 8); //00000100 00000000
private const uint SPECIAL_BYTE = (5 << 8); //00000101 00000000
private const uint SPECIAL_FLOAT = (6 << 8); //00000110 00000000
private const uint SPECIAL_DOUBLE = (7 << 8); //00000111 00000000
private const uint SPECIAL_BYTEARRAY = (8 << 8); //00001000 00000000
private readonly ArraySegment<byte> NullArray = new ArraySegment<byte>(new byte[0]);
private readonly DateTime EPOCH_START_DATETIME = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private readonly SpyMemcachedTranscoderUtils _spyTranscoderUtil = new SpyMemcachedTranscoderUtils(true);
#endregion
#region Serialize/Deserialize
CacheItem ITranscoder.Serialize(object value)
{
return this.Serialize(value);
}
object ITranscoder.Deserialize(CacheItem item)
{
return this.Deserialize(item);
}
protected virtual CacheItem Serialize(object value)
{
// raw data is a special case when some1 passes in a buffer (byte[] or ArraySegment<byte>)
if (value is ArraySegment<byte>)
{
// ArraySegment<byte> is only passed in when a part of buffer is being
// serialized, usually from a MemoryStream (To avoid duplicating arrays
// the byte[] returned by MemoryStream.GetBuffer is placed into an ArraySegment.)
return new CacheItem(SPECIAL_BYTEARRAY, (ArraySegment<byte>)value);
}
var tmpByteArray = value as byte[];
// - or we just received a byte[]. No further processing is needed.
if (tmpByteArray != null)
{
return new CacheItem(SPECIAL_BYTEARRAY, new ArraySegment<byte>(tmpByteArray));
}
uint flags = NOFLAG;
ArraySegment<byte> data;
TypeCode code = value == null ? TypeCode.Empty : Type.GetTypeCode(value.GetType());
switch (code)
{
case TypeCode.Empty:
case TypeCode.DBNull:
flags = SPECIAL_BYTEARRAY;
data = this.SerializeNull();
break;
case TypeCode.String:
flags = NOFLAG;
data = this.SerializeString((String)value);
break;
case TypeCode.Int64:
flags = SPECIAL_LONG;
data = this.SerializeInt64((Int64)value);
break;
case TypeCode.Int32:
flags = SPECIAL_INT;
data = this.SerializeInt32((Int32)value);
break;
case TypeCode.Boolean:
flags = SPECIAL_BOOLEAN;
data = this.SerializeBoolean((Boolean)value);
break;
case TypeCode.DateTime:
flags = SPECIAL_DATE;
data = this.SerializeDateTime((DateTime)value);
break;
case TypeCode.Byte:
flags = SPECIAL_BYTE;
data = this.SerializeByte((byte)value);
break;
case TypeCode.Single: //float
flags = SPECIAL_FLOAT;
data = this.SerializeSingle((float)value);
break;
case TypeCode.Double:
flags = SPECIAL_DOUBLE;
data = this.SerializeDouble((double)value);
break;
default:
flags = SERIALIZED;
data = this.SerializeObject(value);
break;
}
//TODO - determine when to apply compression and do it
return new CacheItem(flags, data);
}
protected virtual object Deserialize(CacheItem item)
{
if (item.Data.Array == null)
return null;
byte[] data = new byte[item.Data.Count];
Array.Copy(item.Data.Array, item.Data.Offset, data, 0, item.Data.Count);
//TODO - compression support
//if ((item.Flags & COMPRESSED) != 0)
//{
// data = Decompress(item.Data);
//}
if ((item.Flags & SERIALIZED) != 0)
{
return DeserializeObject(data);
}
uint flags = item.Flags & SPECIAL_MASK;
if (flags == NOFLAG)
{
return DeserializeString(data);
}
else
{
switch (flags)
{
case SPECIAL_BYTEARRAY:
return data;
case SPECIAL_BOOLEAN:
return this.DeserializeBoolean(data);
case SPECIAL_INT:
return this.DeserializeInt32(data);
case SPECIAL_LONG:
return this.DeserializeInt64(data);
case SPECIAL_DATE:
return this.DeserializeDateTime(data);
case SPECIAL_BYTE:
return this.DeserializeByte(data);
case SPECIAL_FLOAT:
return this.DeserializeSingle(data);
case SPECIAL_DOUBLE:
return this.DeserializeDouble(data);
default:
throw new InvalidOperationException(string.Format("SpyTranscoder undecodable with flags: {0}", flags));
}
}
}
#endregion
#region Typed Serialization
protected virtual ArraySegment<byte> SerializeNull()
{
return NullArray;
}
protected virtual ArraySegment<byte> SerializeString(string value)
{
return new ArraySegment<byte>(Encoding.UTF8.GetBytes((string)value));
}
protected virtual ArraySegment<byte> SerializeByte(byte value)
{
return new ArraySegment<byte>(_spyTranscoderUtil.EncodeByte(value));
}
protected virtual ArraySegment<byte> SerializeBoolean(bool value)
{
return new ArraySegment<byte>(_spyTranscoderUtil.EncodeBoolean(value));
}
protected virtual ArraySegment<byte> SerializeInt32(Int32 value)
{
return new ArraySegment<byte>(_spyTranscoderUtil.EncodeInt(value));
}
protected virtual ArraySegment<byte> SerializeInt64(Int64 value)
{
return new ArraySegment<byte>(_spyTranscoderUtil.EncodeLong(value));
}
protected virtual ArraySegment<byte> SerializeDateTime(DateTime value)
{
var epochMilliseconds = (long)(value - EPOCH_START_DATETIME).TotalMilliseconds;
return new ArraySegment<byte>(_spyTranscoderUtil.EncodeLong(epochMilliseconds));
}
protected virtual ArraySegment<byte> SerializeDouble(Double value)
{
return new ArraySegment<byte>(_spyTranscoderUtil.EncodeLong(BitConverter.DoubleToInt64Bits(value)));
}
protected virtual ArraySegment<byte> SerializeSingle(Single value)
{
return new ArraySegment<byte>(_spyTranscoderUtil.EncodeLong(BitConverter.ToInt32(BitConverter.GetBytes(value), 0)));
}
protected virtual ArraySegment<byte> SerializeObject(object value)
{
using (var ms = new MemoryStream())
{
new BinaryFormatter().Serialize(ms, value);
return new ArraySegment<byte>(ms.GetBuffer(), 0, (int)ms.Length);
}
}
#endregion
#region Typed deserialization
protected virtual String DeserializeString(byte[] value)
{
//return Encoding.UTF8.GetString(value.Array, value.Offset, value.Count);
return Encoding.UTF8.GetString(value);
}
protected virtual Boolean DeserializeBoolean(byte[] value)
{
return _spyTranscoderUtil.DecodeBoolean(value);
}
protected virtual Int32 DeserializeInt32(byte[] value)
{
return _spyTranscoderUtil.DecodeInt(value);
}
protected virtual Int64 DeserializeInt64(byte[] value)
{
return _spyTranscoderUtil.DecodeLong(value);
}
protected virtual DateTime DeserializeDateTime(byte[] value)
{
var epochMilliseconds = _spyTranscoderUtil.DecodeLong(value);
return EPOCH_START_DATETIME.AddMilliseconds(epochMilliseconds);
}
protected virtual Double DeserializeDouble(byte[] value)
{
return BitConverter.Int64BitsToDouble(_spyTranscoderUtil.DecodeLong(value));
}
protected virtual Single DeserializeSingle(byte[] value)
{
byte[] bytes = BitConverter.GetBytes(_spyTranscoderUtil.DecodeInt(value));
return BitConverter.ToSingle(bytes, 0);
}
protected virtual Byte DeserializeByte(byte[] data)
{
return _spyTranscoderUtil.DecodeByte(data);
}
protected virtual object DeserializeObject(byte[] value)
{
//using (var ms = new MemoryStream(value.Array, value.Offset, value.Count))
using (var ms = new MemoryStream(value))
{
return new BinaryFormatter().Deserialize(ms);
}
}
#endregion
#region GZip
private ArraySegment<byte> Compress(ArraySegment<byte> data)
{
using (var outStream = new MemoryStream())
{
using (var compressStream = new GZipStream(outStream, CompressionMode.Compress))
{
using (var inStream = new MemoryStream(data.Array))
{
inStream.CopyTo(compressStream);
return new ArraySegment<byte>(outStream.ToArray());
}
}
}
}
private ArraySegment<byte> Decompress(ArraySegment<byte> data)
{
using (var inStream = new MemoryStream(data.Array))
{
using (var decompressStream = new GZipStream(inStream, CompressionMode.Decompress))
{
using (var outStream = new MemoryStream())
{
decompressStream.CopyTo(outStream);
return new ArraySegment<byte>(outStream.ToArray());
}
}
}
}
#endregion
}
internal class SpyMemcachedTranscoderUtils
{
private readonly bool _packZeros;
public SpyMemcachedTranscoderUtils(bool pack = true)
{
_packZeros = pack;
}
public byte[] EncodeNum(long value, int maxBytes)
{
byte[] rv = new byte[maxBytes];
for (int i = 0; i < rv.Length; i++)
{
int pos = rv.Length - i - 1;
rv[pos] = (byte)((value >> (8 * i)) & 0xff);
}
if (_packZeros)
{
int firstNon0 = 0;
// Just looking for what we can reduce
while (firstNon0 < rv.Length && rv[firstNon0] == 0)
{
firstNon0++;
}
if (firstNon0 > 0)
{
byte[] tmp = new byte[rv.Length - firstNon0];
Array.Copy(rv, firstNon0, tmp, 0, rv.Length - firstNon0);
rv = tmp;
}
}
return rv;
}
public byte[] EncodeLong(long value)
{
return EncodeNum(value, 8);
}
public long DecodeLong(byte[] value)
{
long rv = 0;
foreach (byte i in value)
{
rv = (rv << 8) | (i < 0 ? 256 + i : i);
}
return rv;
}
public byte[] EncodeInt(int value)
{
return EncodeNum(value, 4);
}
public int DecodeInt(byte[] value)
{
if (value.Length > 4)
throw new InvalidOperationException("Too long to be an int (" + value.Length + ") bytes");
return (int)DecodeLong(value);
}
public byte[] EncodeByte(byte value)
{
return new byte[] { value };
}
public byte DecodeByte(byte[] value)
{
if (value.Length > 1)
throw new InvalidOperationException("Too long for a byte");
byte rv = 0;
if (value.Length == 1)
{
rv = value[0];
}
return rv;
}
public byte[] EncodeBoolean(bool b)
{
byte[] rv = new byte[1];
rv[0] = (byte)(b ? '1' : '0');
return rv;
}
public bool DecodeBoolean(byte[] value)
{
if (value.Length != 1)
throw new InvalidOperationException("Wrong length for a boolean");
return value[0] == '1';
}
}