Silverclaw
Silverclaw

Reputation: 1396

Is there a direct way to calculate size of primitive fields?

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.

Upvotes: 2

Views: 1525

Answers (3)

Holger
Holger

Reputation: 298233

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.

Upvotes: 2

JackDaniels
JackDaniels

Reputation: 1071

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.

Upvotes: -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.

Upvotes: 1

Related Questions