google / flatbuffers

FlatBuffers: Memory Efficient Serialization Library
https://flatbuffers.dev/
Apache License 2.0
23.28k stars 3.25k forks source link

[FlatBuffers: object serialization must not be nested] message is necessary ? #301

Closed hash-X closed 9 years ago

hash-X commented 9 years ago

Here is the schema

table ClassInfo {
    numTeacher:short;
    stu:[Student];
}

table Student {
     name:string;
     age:short;
     id:int;
}

and I will have two ways of Serialization and Deserialization about table ClassInfo. methodOne have no problem, But methodTwo will throw a message of "Exception in thread "main" java.lang.AssertionError: FlatBuffers: object serialization must not be nested." So I remove the notNested() in FlatBufferBuilder and then get the right answer. So I am not sure this remind is necessary. If necessary,Why ? How can I fix my code [ methodTwo ] and run the problem collectly ? Thank you !

package test.genstudent;

import com.google.flatbuffers.FlatBufferBuilder;
import src.genstudent.ClassInfo;
import src.genstudent.Student;

/**
 * Created by minglei on 15-10-20.
 */
public class TestStudent1 {

  static void methodOne(FlatBufferBuilder fbb) {
    int[] data =
        {
            Student.createStudent(fbb, fbb.createString("zhangsan"),
                (short)19,
                1)};
    int stu = 0;

    stu = ClassInfo.createStuVector(fbb, data);
    int classInfoOffset = ClassInfo.createClassInfo(fbb, (short)3, stu);
    ClassInfo.finishClassInfoBuffer(fbb, classInfoOffset);

    ClassInfo classInfo = ClassInfo.getRootAsClassInfo(fbb.dataBuffer());
    String name = classInfo.stu(0).name();
    System.out.println(name);
  }
  // it is a wrong method
  static void methodTwo(FlatBufferBuilder fbb) {
    ClassInfo.startClassInfo(fbb);
    ClassInfo.addNumTeacher(fbb, (short)3);
    int stuOffset = Student.createStudent(fbb,
        fbb.createString("lisi"), (short)19, 1);
    ClassInfo.addStu(fbb, stuOffset);
    int env = ClassInfo.endClassInfo(fbb);
    ClassInfo.finishClassInfoBuffer(fbb, env);
    ClassInfo classInfo = ClassInfo.getRootAsClassInfo(fbb.dataBuffer());
    String name = classInfo.stu(0).name();
    System.out.println(name);
  }

  public static void main(String[] args) {
    FlatBufferBuilder fbb = new FlatBufferBuilder();
//    methodOne(fbb);
    methodTwo(fbb);
  }
}
hash-X commented 9 years ago

methodTwo(fbb) will throw an error, I am not sure this message is necessary. I am confused.

Exception in thread "main" java.lang.AssertionError: FlatBuffers: object serialization must not be nested.
    at com.google.flatbuffers.FlatBufferBuilder.notNested(FlatBufferBuilder.java:293)
    at com.google.flatbuffers.FlatBufferBuilder.startVector(FlatBufferBuilder.java:239)
    at com.google.flatbuffers.FlatBufferBuilder.createString(FlatBufferBuilder.java:266)
    at test.genstudent.TestStudent1.methodTwo(TestStudent1.java:32)
    at test.genstudent.TestStudent1.main(TestStudent1.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
ghost commented 9 years ago

It can work with small amounts of data, but if your nested tables exceed 64k you'll run into trouble. Also your serialized data is less efficient this way (more vtables, more cache misses).

So instead of trying to ignore the error, serialize your data in the correct order, depth-first (post order), i.e. call createStudent before startClassInfo.

hash-X commented 9 years ago

@gwvo Thank you for your reply. I just fixed my code according to your suggestion. But It seems still have problem. I don't know why ? Here is the latest code. Could you help me ? How can I fix the code and it looks correctly ?

static void methodTwo(FlatBufferBuilder fbb) {
    int stuOffset = Student.createStudent(fbb,
        fbb.createString("lisi"), (short)19, 1);
    ClassInfo.startClassInfo(fbb);
    ClassInfo.addStu(fbb, stuOffset);
    ClassInfo.addNumTeacher(fbb, (short) 3);
    int env = ClassInfo.endClassInfo(fbb);
    ClassInfo.finishClassInfoBuffer(fbb, env);
    ClassInfo classInfo = ClassInfo.getRootAsClassInfo(fbb.dataBuffer());
    short num = classInfo.numTeacher();
    System.out.println(num);
    System.out.print(classInfo.stu(0).name());
  }

And here is the error

Exception in thread "main" java.lang.IndexOutOfBoundsException
    at java.nio.Buffer.checkIndex(Buffer.java:538)
    at java.nio.HeapByteBuffer.getInt(HeapByteBuffer.java:359)
    at com.google.flatbuffers.Table.__offset(Table.java:33)
    at src.genstudent.Student.name(Student.java:16)
    at test.genstudent.TestStudent1.methodTwo(TestStudent1.java:43)
    at test.genstudent.TestStudent1.main(TestStudent1.java:49)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
ghost commented 9 years ago

field stu needs to be a [Student], but you serialize it as a Student.

hash-X commented 9 years ago

@gwvo , OK, I know how to fix my code and run it collectly. Thank you very much.

 int[] data =
        {
            Student.createStudent(fbb, fbb.createString(" gwvo "),
                (short)19,
                1)};