2010-03-02 2 views
0

Я пытаюсь использовать Preon для разбора двоичных файлов, которые структурированы как последовательность записей переменной длины. Для каждой записи есть число, которое определяет длину записи (в байтах).Разбор переменных длины записей в Preon

Вот упрощенная версия того, что я пытаюсь сделать:

package test.preon; 

import nl.flotsam.preon.annotation.BoundList; 
import nl.flotsam.preon.annotation.BoundNumber; 
import java.util.List; 

public class BinFile { 
    @BoundNumber(size="16") int numberOfRecords; 
    @BoundList(type=Record.class, size="numberOfRecords") List<Record> records; 

    public int getNumberOfRecords() { 
     return numberOfRecords; 
    } 

    public List<Record> getRecords() { 
     return records; 
    } 

    public class Record { 
     @BoundNumber(size="16") int recordLength; 
     @BoundList(size="recordLength") byte[] data; 

     public int getRecordLength() { 
      return recordLength; 
     } 

     public byte[] getData() { 
      return data; 
     } 
    } 
} 

Таким образом, numberOfRecords определяет количество записей в файле и Recordlength определяет длину каждой записи. Проблема в том, что Preon не может разрешить recordLength в записи, хотя numberOfRecords отлично работает в BinFile.

Вот исключение я получаю:

nl.flotsam.limbo.BindingException: Failed to resolve recordLength on class test.preon.BinFile 
at nl.flotsam.preon.codec.BindingsContext$BindingsResolver.get(BindingsContext.java:412) 
at nl.flotsam.preon.codec.BindingsContext$BindingReference.resolve(BindingsContext.java:247) 
at nl.flotsam.preon.codec.BindingsContext$BindingReference.resolve(BindingsContext.java:189) 
at nl.flotsam.limbo.ast.ReferenceNode.eval(ReferenceNode.java:57) 
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$5.eval(ArithmeticNode.java:109) 
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250) 
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33) 
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$3.eval(ArithmeticNode.java:83) 
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250) 
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33) 
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$5.eval(ArithmeticNode.java:109) 
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250) 
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33) 
at nl.flotsam.preon.codec.ListCodecFactory$SwitchingListCodec.decode(ListCodecFactory.java:458) 
at nl.flotsam.preon.codec.ListCodecFactory$SwitchingListCodec.decode(ListCodecFactory.java:443) 
at nl.flotsam.preon.binding.StandardBindingFactory$FieldBinding.load(StandardBindingFactory.java:128) 
at nl.flotsam.preon.codec.ObjectCodecFactory$ObjectCodec.decode(ObjectCodecFactory.java:251) 
at nl.flotsam.preon.DefaultCodecFactory$DefaultCodec.decode(DefaultCodecFactory.java:173) 
at nl.flotsam.preon.Codecs.decode(Codecs.java:218) 
at nl.flotsam.preon.Codecs.decode(Codecs.java:199) 
    ... 

Если изменить размер = "Recordlength" постоянной, например, size = "42", я не получаю исключения (но, конечно, тогда длина записи всегда должна быть одинаковой).

Есть ли другой способ для меня сделать переменную длины записи, или я должен организовывать вещи по-другому?

Если кто-то заинтересован, вот тест JUnit Я использовал:

package test.preon; 

import org.junit.Test; 
import static org.junit.Assert.*; 
import nl.flotsam.preon.Codecs; 
import nl.flotsam.preon.Codec; 
import nl.flotsam.preon.DecodingException; 
import test.preon.BinFile; 
import test.preon.BinFile.Record; 
import java.util.List; 

public class BinFileTest { 

    @Test 
    public void parseBinFile() throws DecodingException { 
     Codec<BinFile> codec = Codecs.create(BinFile.class); 
     byte[] buffer = new byte[] { 
       2, 0, 
       3, 0, 
       'a', 'b', 'c', 
       4, 0, 
       '1', '2', '3', '4' 
     }; 
     BinFile b = Codecs.decode(codec, buffer); 

     assertEquals(b.getNumberOfRecords(), 2); 

     List<Record> rL = b.getRecords(); 

     assertEquals(rL.size(), 2); 

     Record r0 = rL.get(0); 
     assertEquals(r0.getRecordLength(), 3); 
     assertEquals(new String(r0.getData()), "abc"); 

     Record r1 = rL.get(1); 
     assertEquals(r1.getRecordLength(), 4); 
     assertEquals(new String(r1.getData()), "1234"); 
    } 
} 

ответ

1

Оказывается, вы столкнулись с ошибкой. В ListCodecFactory есть политика для определения того, какой тип кодека генерируется при различных обстоятельствах, и оказывается, что в этом случае он выбирает неправильный. У меня есть патч для него, и я могу отправить его вам, если вы заинтересованы.

+0

создал отчет об ошибке для него здесь: http://kenai.com/jira/browse/PREON-9 –

+0

Attached патч к этому вопросу. –

+0

Удивительный! Благодарю. – skoob

1

Создано два отчета об ошибке: PREON-16 и PREON-17. Первый решает проблему, описанную выше. Вторая проблема решает небольшую проблему, которая стоит упомянуть здесь:

В приведенном ниже коде размер элемента Test2 в списке, называемом «записями», полностью определяется Test1. (Размер Test2 - это в основном количество символов в Test2.value, которое определяется атрибутом «nrCharacters» Test1.)

Как следствие этого Preon может оптимизировать чтение списка записей. Ему не нужно читать все записи сразу; вместо этого он может пропустить его и прочитать эти элементы только в случае необходимости. (Исходное положение элемента в основном является функцией индекса элементов.)

Сложность состоит в том, что размер элемента должен быть рассчитан до того, как будет прочитан экземпляр Test2. Поскольку он содержит ссылки, основанные на контексте Test2, эти ссылки необходимо переписать. Фактически, выражение полного размера (хотя в этом случае и простое) должно быть переписано. Это решается в PREON-17.

public static class Test1 { 

     @BoundNumber(size = "8") 
     public int nrRecords; 

     @BoundNumber(size = "8") 
     public int nrCharacters; 

     @BoundList(size = "nrRecords", type = Test2.class) 
     public List<Test2> records; 

     public static class Test2 { 

      @BoundString(size = "outer.nrCharacters") 
      public String value; 

     } 

    } 
Смежные вопросы