2

I'm writing a small serialization utility and I would like to calculate how many bytes I need to reserve to write an object to a byte array. Let's say I have a class with a few fields/getters like

int age;
String name;
Colour colour;
boolean active;
...

I thought I could use reflection to iterate over fields and look what size they have, but all I can do is check whether they are primitive. Which is a start, since I would use a short as an offset for Objects. Okay, I could use

if(field.getType() == int.class) {
    size = Integer.BYTES;
}

or use a map. It would not be that hard, since I only need to know the size of primitives. But out of curiosity: Is there any direct way?

edit to clarify: I am using it to serialize data, basically like DataOutput/InputStream, but with some tweaks I wanted for myself.

The API class looks like this:

public final void write(int i) {
    checkBounds(Integer.BYTES);
    PrimitiveWriteUtil.write(i, data, marker);
    advance(Integer.BYTES);
}

The actual work is done by a utility class:

public static void write(int i, byte[] target, int pos) {
    target[pos] =       (byte) ((i >>> 24) & 0xFF);
    target[pos + 1] =   (byte) ((i >>> 16) & 0xFF);
    target[pos + 2] =   (byte) ((i >>>  8) & 0xFF);
    target[pos + 3] =   (byte) ((i >>>  0) & 0xFF);
}

So, I want to know the required size of my byte array, so I can avoid the effort of checking and resizing later.

Silverclaw
  • 1,316
  • 2
  • 15
  • 28
  • possible duplicate of : http://stackoverflow.com/questions/52353/in-java-what-is-the-best-way-to-determine-the-size-of-an-object – Suresh Atta Jul 11 '16 at 08:23
  • I looked through that before, and it absolutely does not match my use case. I only want to get the size of all the primitive fields of _one_ object at runtime. – Silverclaw Jul 11 '16 at 08:44
  • Why? Just write to a ByteArrayOutputStream and get the byte array from that. It will be the right size. – user207421 Jul 14 '16 at 03:44
  • Well, because it would be totally inefficient to write to a ByteArrayOutputStream, just to determine the size, for actually writing to an optimized format... – Silverclaw Jul 14 '16 at 07:23

3 Answers3

2

Well, you already know about the BYTES field, the problem is, these fields are declared in the corresponding wrapper types and there is no simple way of getting the wrappers type for a primitive type. In terms of code size, the simplest way to get it, is to read the field via the get method which implies wrapping of the primitive value:

public class FieldSize {
    static final int BOOLEAN_SIZE = 1; // adjust to your storage method
    static final int REF_SIZE = 2;     // dito
    private static int getSize(Field f, Object o) {
        try {
        return f.getType().isPrimitive()? f.getType()==boolean.class? BOOLEAN_SIZE:
               f.get(o).getClass().getField("BYTES").getInt(null): REF_SIZE;
        } catch(ReflectiveOperationException ex) {
            throw new AssertionError(ex);
        }
    }
    boolean z;
    byte b;
    short s;
    char c;
    int i;
    long l;
    float f;
    double d;
    String str;

    public static void main(String[] args) {
        System.out.println("total: "+calculateSize(new FieldSize())+" size");
    }
    static int calculateSize(Object o) {
        int total=0;
        for(Class cl=o.getClass(); cl!=null; cl=cl.getSuperclass()) {
            for(Field f: cl.getDeclaredFields()) {
                if(Modifier.isStatic(f.getModifiers())) continue;
                int size=getSize(f, o);
                System.out.println(f.getName()+" ("+f.getType()+"): "+size);
                total+=size;
            }
        }
        return total;
    }
}

However, in terms of underlying operations, it is not the simplest solution and if you don’t like it, I fully understand that.

In that case, the answer is that there is no simple/ direct way of getting these numbers, so doing either, a mapping from type to number or linearly probing for these eight possibilities, is unavoidable and hence, also the simplest (available) solution.

Holger
  • 285,553
  • 42
  • 434
  • 765
1

The size of the primitives is defined in JLS 4.2.

byte is 1 byte, short and char are 2, int and float are 32, double and long are 64.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • 1
    Yes...and as I stated, I could just use a mapping to Short.Bytes, Integer.Bytes, etc. My question is whether that's necessary or if there is an alternative way to **directly** get a field's size. – Silverclaw Jul 11 '16 at 08:40
-1

If you need to use it for any more purpose than a mere theoretical exercise, relying on the precise number may be dangerous.

Since you want to use it for allocating the size of the byte array for serialization, consider using ByteArrayOutputStream.

ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bytesOut);

out.writeObject(new MyObject(12, "JackDaniels", true));
out.close();
System.out.println("Number of bytes for serialized object: " + bytesOut.toByteArray().length);

Full working code with the MyObject helper class at Codiva online compiler IDE.

This does not answer your question directly, but I believe this is the correct way to achieve what you are trying to do.


You can modify how the serialization happens by implementing readObject and writeObject methods. For an example, take a look at http://www.byteslounge.com/tutorials/java-custom-serialization-example.

JackDaniels
  • 985
  • 9
  • 26
  • Relying on the size isn't dangerous. This is Java, not C. – chrylis -cautiouslyoptimistic- Jul 11 '16 at 09:15
  • Not true. In C, at least, you have sizeof() that is reliable at run time (of course, you should watch for pointers). In Java, there is no trusted way to estimate the size of an object. Guess what is the size of boolean that is not specified in JLS ? How about boolean array? or even size of int array. – JackDaniels Jul 11 '16 at 09:23
  • Objects and arrays will be handled differently anyway. All I need is size of primitives, which is defined by constant values in their wrapper classes. I just hoped there would be a way to avoid using a map or switch to get the size. – Silverclaw Jul 11 '16 at 09:25