If I serialize the exact analogous of an array and a vector into ByteArrays, I get almost twice the size for the vector. Check this code:
var arr:Array = [];
var vec:Vector.<int> = new Vector.<int>;
for (var i:int = 0; i < 100; ++i) {
arr.push(i);
vec.push(i);
}
var b:ByteArray;
b = new ByteArray();
b.writeObject(arr);
trace("arr",b.length); // arr 204
b = new ByteArray();
b.writeObject(vec);
trace("vec",b.length); // vec 404
Seems like a buggy or unoptimized implementation on Adobe's behalf..? Or am I missing something here?
According to AMF 3 specification AMF stores arrays as a separate, optimized for dense arrays manner, and also stores ints in an optimized manner to minimize the number of bytes used for small ints. This way, your array of values 0-99 gets stored as:
00000000 0x09 ; array marker
00000001 0x81 0x49 ; array length in U29A format = 100
00000003 0x01 ; UTF8-empty - this array is all dense
00000004 to 000000CB - integer data, first integer marker 0x04, then an integer in U29.
All your ints are less than 128, so they are represented as single byte equal to the integer. This makes a small int in an array take 2 bytes instead of full 4 or even 8 for large integers.
Now, for Vector, as Vector is dense by default, it's better to not convert an integer to U29 format, because anything bigger than 0x40000000 gets converted to DOUBLE (aka Number) and this is bad for vectors, so a Vector.<int> is stored as is, with 4 bytes per integer inside a vector.
00000000 0x0D ; vector-int marker
00000001 0x81 0x49 ; U29 length of the vector = 100
00000003 0x00 ; fixed vector marker, 0 is not fixed
00000004 to 00000193 - integers in U32
So, for small integers an array takes less space than a Vector of ints, but for large integers an array can take up to 9 bytes per int stored, while a Vector will always use 4 bytes per integer.
Consider the following alteration to your code:
var arr:Array = [];
var vec:Vector.<int> = new Vector.<int>;
for (var i:int = 0; i < 100; ++i) {
var x:int=Math.floor(Math.random()*4294967295); // 0 to 0xFFFFFFFE
arr.push(x);
vec.push(x);
trace(x);
}
var b:ByteArray;
b = new ByteArray();
b.writeObject(arr);
trace("arr",b.length); // some variable value will be shown, up to 724
b = new ByteArray();
b.writeObject(vec);
trace("vec",b.length); // here the value will always be 404
Related
I'm trying to generate a 3d mesh using 3d RLE binary mask.
In itk, I find a class named itkBinaryMask3DMeshSource
it's based on MarchingCubes algorithm
some example, use this class, ExtractIsoSurface et ExtractIsoSurface
in my case, I have a rle 3D binary mask but represented in 1d vector format.
I'm writing a function for this task.
My function takes as parameters :
Inputs : crle 1d vector ( computed rle), dimension Int3
Output : coord + coord indices ( or generate a single file contain both of those array; and next I can use them to visualize the mesh )
as a first step, I decoded this computed rle.
next, I use imageIterator to create an image compatible to BinaryMask3DMeshSource.
I'm blocked, in the last step.
This is my code :
void GenerateMeshFromCrle(const std::vector<int>& crle, const Int3 & dim,
std::vector<float>* coords, std::vector<int>*coord_indices, int* nodes,
int* cells, const char* outputmeshfile) {
std::vector<int> mask(crle.back());
CrleDecode(crle, mask.data());
// here we define our itk Image type with a 3 dimension
using ImageType = itk::Image< unsigned char, 3 >;
ImageType::Pointer image = ImageType::New();
// an Image is defined by start index and size for each axes
// By default, we set the first start index from x=0,y=0,z=0
ImageType::IndexType start;
start[0] = 0; // first index on X
start[1] = 0; // first index on Y
start[2] = 0; // first index on Z
// until here, no problem
// We set the image size on x,y,z from the dim input parameters
// itk takes Z Y X
ImageType::SizeType size;
size[0] = dim.z; // size along X
size[1] = dim.y; // size along Y
size[2] = dim.x; // size along Z
ImageType::RegionType region;
region.SetSize(size);
region.SetIndex(start);
image->SetRegions(region);
image->Allocate();
// Set the pixels to value from rle
// This is a fast way
itk::ImageRegionIterator<ImageType> imageIterator(image, region);
int n = 0;
while (!imageIterator.IsAtEnd() && n < mask.size()) {
// Set the current pixel to the value from rle
imageIterator.Set(mask[n]);
++imageIterator;
++n;
}
// In this step, we launch itkBinaryMask3DMeshSource
using BinaryThresholdFilterType = itk::BinaryThresholdImageFilter< ImageType, ImageType >;
BinaryThresholdFilterType::Pointer threshold =
BinaryThresholdFilterType::New();
threshold->SetInput(image->GetOutput()); // here it's an error, since no GetOutput member for image
threshold->SetLowerThreshold(0);
threshold->SetUpperThreshold(1);
threshold->SetOutsideValue(0);
using MeshType = itk::Mesh< double, 3 >;
using FilterType = itk::BinaryMask3DMeshSource< ImageType, MeshType >;
FilterType::Pointer filter = FilterType::New();
filter->SetInput(threshold->GetOutput());
filter->SetObjectValue(1);
using WriterType = itk::MeshFileWriter< MeshType >;
WriterType::Pointer writer = WriterType::New();
writer->SetFileName(outputmeshfile);
writer->SetInput(filter->GetOutput());
}
any idea
I appreciate your time.
Since image is not a filter you can plug it in directly: threshold->SetInput(image);. At the end of this function, you also need writer->Update();. The rest looks good.
Side-note: it looks like you might benefit from usage of import filter instead of manually iterating the buffer and copying values one at a time.
var fData:ByteArray = new ByteArray();
I need to remove some bytes in this array, but can't find any public method in Flash to do that.
I searched something like fData.remove(start,length) but with no success.
here is a code
function _dlProgressHandler(evt:ProgressEvent):void { //this is progressEvent for URLStream
............... ///some code
var ff:ByteArray = new ByteArray();
stream.readBytes(ff,0,stream.bytesAvailable);
fileData.writeBytes(ff,0,ff.length); //stream writes into fileData byteArray
//and here is cutter:
fileData.position=0;
fileData.writeBytes(ff,100,fileData.length);
fileData.length=fileData.length-100);
}
So, fileData cut itself unpredictably sometimes.
Sometimes old blocks're found twice, sometimes they're not found at all.
You can always just only read the bytes that you want, which will have the same effect as discarding the bytes that you don't want. As a very simple example, assume you have a ByteArray that is 10 bytes long and you want to discard the first 3 bytes:
var newBytes:ByteArray = new ByteArray();
newBytes.writeBytes(fData, 2, 7);
So instead of removing the bytes that you don't want from fData, you just create a new ByteArray and only get they bytes that you want from fData.
Obviously, if the sequence of bytes you want to remove is not just a sequence from the beginning or end of fData it will be a little more complicated, but the method remains the same: read the bytes that you want, instead of removing the ones you don't.
AS-3 is actually very nice sometimes. This removes bytes from your array anywhere you want. Beginning, middle or the end. Just need to check indices to avoid IndexOutOfBounds
var array: ByteArray = ...; // create the byte array or load one
var index: int = 4;
var count: int = 5;
array.position = index;
array.writeBytes(array, index + count, array.length - (index + count));
array.length = array.length - count;
I tested this and it works well, just the checks are missing
a byte array can write to itself
I want to pack epoch milliseconds into 6 bytes but i have problem. Let me introduce it:
trace(t);
for (var i:int = 6; i > 0; i--) {
dataBuffer.writeByte(((t >>> 8*(i-1)) & 255));
trace(dataBuffer[dataBuffer.length - 1]);
}
Output:
1330454496254
131
254
197
68
131
254
What i'm doing wrong?
I'm just guessing but I think your t variable is getting automatically converted to an int before the bit operation takes effect. This, of course, destroys the value.
I don't think it's possible to use Number in bit operations - AS3 only supports those with int-s.
Depending on how you acquire the value in t, you may want to start with 2 int-s and then extrat the bytes from those.
The Number type is an IEEE 754 64-bit double-precision number, which is quite a different format to your normal int. The bits aren't lined up quite the same way. What you're looking for is a ByteArray representation of a normal 64-bit int type, which of course doesn't exist in ActionScript 3.
Here's a function that converts a Number object into its "int64" equivalent:
private function numberToInt64Bytes(n:Number):ByteArray
{
// Write your IEEE 754 64-bit double-precision number to a byte array.
var b:ByteArray = new ByteArray();
b.writeDouble(n);
// Get the exponent.
var e:int = ((b[0] & 0x7F) << 4) | (b[1] >> 4);
// Significant bits.
var s:int = e - 1023;
// Number of bits to shift towards the right.
var x:int = (52 - s) % 8;
// Read and write positions in the byte array.
var r:int = 8 - int((52 - s) / 8);
var w:int = 8;
// Clear the first two bytes of the sign bit and the exponent.
b[0] &= 0x80;
b[1] &= 0xF;
// Add the "hidden" fraction bit.
b[1] |= 0x10;
// Shift everything.
while (w > 1) {
if (--r > 0) {
if (w < 8)
b[w] |= b[r] << (8 - x);
b[--w] = b[r] >> x;
} else {
b[--w] = 0;
}
}
// Now you've got your 64-bit signed two's complement integer.
return b;
}
Note that it works only with integers within a certain range, and it doesn't handle values like "not a number" and infinity. It probably also fails in other cases.
Here's a usage example:
var n:Number = 1330454496254;
var bytes:ByteArray = numberToInt64Bytes(n);
trace("bytes:",
bytes[0].toString(16),
bytes[1].toString(16),
bytes[2].toString(16),
bytes[3].toString(16),
bytes[4].toString(16),
bytes[5].toString(16),
bytes[6].toString(16),
bytes[7].toString(16)
);
Output:
bytes: 0 0 1 35 c5 44 83 fe
It should be useful for serializing data in AS3 later to be read by a Java program.
Homework assignment: Write int64BytesToNumber()
i have to read a sequence of bytes,that was written in different ways (writeBite, writeShort and writeMultiByte) and display them has list of HEX byte on video.
My problem is convert the number 1500, i tryed other number and the results was correct...
here is a an example:
var bytes:Array = [];
var ba:ByteArray = new ByteArray();
ba.writeShort(1500);
ba.position = 0;
for (var i=0; i<ba.length; i++)
{
bytes.push(ba.readByte().toString(16));
}
trace(bytes);//5,-24 i'm expetting 5,DC
The method readByte reads a signed byte (ranges from -128 to 127). The most significant bit defines the sign. In case of numbers greater than 127 (like DC) that bit will be 1 and the number will be seen as a negative number. The two's complement of the negative byte is used to get the signed value. In case of DC, which is 1101 1100 in binary the complement would be 0010 0011 which is 23. A one is added and the value will be regarded as negative, which will give you the -24 you are seeing.
You should use readUnsignedByte to read values from 0 to 255.
As there is no real Byte type in AS3, readByte() returns an int. You can try this instead:
for (var i=0; i<ba.length; i++)
{
bytes.push(ba[i].toString(16));
}
var bytes:ByteArray = new ByteArray;
bytes.writeInt(0);
trace(bytes.length); // prints 4
trace(bytes.toString().length); // prints 4
When I run the above code the output suggests that every character in the string returned by toString contains one byte from the ByteArray. This is of course great if you want to display the content of the ByteArray, but not so great if you want to send its content encoded in a string and the size of the string matters.
Is it possible to get a string from the ByteArray where every character in the string contains two bytes from the ByteArray?
You can reinterpret your ByteArray as containing only shorts. This lets you read two bytes at a time and get a single number value representing them both. Next, you can take these numbers and reinterpret them as being character codes. Finally, create a String from these character codes and you're done.
public static function encode(ba:ByteArray):String {
var origPos:uint = ba.position;
var result:Array = new Array();
for (ba.position = 0; ba.position < ba.length - 1; )
result.push(ba.readShort());
if (ba.position != ba.length)
result.push(ba.readByte() << 8);
ba.position = origPos;
return String.fromCharCode.apply(null, result);
}
There is one special circumstance to pay attention to. If you try reading a short from a ByteArray when there is only one byte remaining in it, an exception will be thrown. In this case, you should call readByte with the value shifted 8 bits instead. This is the same as if the original ByteArray had an extra 0 byte at the end. (making it even in length)
Now, as for decoding this String... Get the character code of each character, and place them into a new ByteArray as shorts. It will be identical to the original, except if the original had an odd number of bytes, in which case the decoded ByteArray will have an extra 0 byte at the end.
public static function decode(str:String):ByteArray {
var result:ByteArray = new ByteArray();
for (var i:int = 0; i < str.length; ++i) {
result.writeShort(str.charCodeAt(i));
}
result.position = 0;
return result;
}
In action:
var ba:ByteArray = new ByteArray();
ba.writeInt(47);
ba.writeUTF("Goodbye, cruel world!");
var str:String = encode(ba);
ba = decode(str);
trace(ba.readInt());
trace(ba.readUTF());
Your question is a bit confusing. You have written a 4 byte int to your ByteArray. You haven't written any characters (unicode or other) to it. If you want to pass text, write text and pass it as UTF8. It will take less space than having two bytes for every character, at least for most western languages.
But honestly, I'm not sure I understood what you are trying to accomplish. Do you want to send numbers or text? What backend are you talking to? Do you need a ByteArray at all?