последовательность переменной длины, первый бит указывает на конец последовательности, предварительно

Как бы вы проанализировали последовательность байтов переменной длины, где первый бит (BigEndian) указывает, следует ли другой байт, используя Preon?

Пример

    byte[] bytecode = new byte[] {
            (byte) 0xf2, (byte) 0xbf, (byte) 0xbf, (byte) 0xbf, (byte) 0x50
    };

Примечания

  • первый бит, который указывает, что следующий отбрасывается в окончательной полезной нагрузке
  • версия Preon, используемая для этого поста, была 1.1.

Байты результата (в десятичном формате)

{ 114, 63, 63, 63, 80 }

Уже пробовал

@BoundList + @Choices(с условиями)

Limbo exp lang не поддерживает вызовы методов, поэтому вы не можете определить конец потока (предыдущий блок должен иметь знак 1, а текущий блок должен быть последним, т. е. знак должен быть равен 0)

Рекурсивный подход с @If

public static class Entry {

    @BoundNumber(size = "1", byteOrder = ByteOrder.BigEndian)
    private byte hasNext;

    @BoundNumber(size = "7", byteOrder = ByteOrder.BigEndian)
    private byte payload;

    @If("hasNext > 0")
    @BoundNumber(size = "1", byteOrder = ByteOrder.BigEndian)
    private byte hasNext1;

    @If("hasNext > 0")
    @BoundNumber(size = "7", byteOrder = ByteOrder.BigEndian)
    private byte payload1;

    @If("hasNext1 > 0")
    @BoundObject
    private Entry nextEntry;

    @Override
    public String toString() {
        return hasNext > 0 ? String.valueOf(payload) : String.valueOf(payload)
                + ", " + String.valueOf(payload1);
    }

    //...
}

По какой-то причине, например, упомянутой выше, Preon будет анализировать только 2 экземпляра Entry (родительский и дочерний), даже когда их должно быть 3.

Спасибо.


person Dusan    schedule 10.02.2011    source источник


Ответы (1)


Для этого я бы предложил реализовать собственный кодек или кодекдекоратор, в зависимости от того, что вы хотите сделать. Если все, что вы хотите сделать, это сохранить последовательность байтов в своем собственном массиве байтов, то создание собственного кодека и подключение его к платформе должно быть довольно простым.

Вот реализация кодека, которая, вероятно, близка к тому, что вы ищете:

VariableLengthByteArrayCodec:

package org.codehaus.preon.sample.varlength;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.codehaus.preon.Builder;
import org.codehaus.preon.Codec;
import org.codehaus.preon.CodecDescriptor;
import org.codehaus.preon.DecodingException;
import org.codehaus.preon.Resolver;
import org.codehaus.preon.buffer.BitBuffer;
import org.codehaus.preon.channel.BitChannel;
import org.codehaus.preon.el.Expression;

import nl.flotsam.pecia.Documenter;
import nl.flotsam.pecia.ParaContents;
import nl.flotsam.pecia.SimpleContents;

public class VariableLengthByteArrayCodec implements Codec<byte[]> {

    public byte[] decode(BitBuffer buffer, Resolver resolver, Builder builder) throws DecodingException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        boolean cont = true;
        while (cont) {
            byte b = buffer.readAsByte(8);
            bout.write(b);
            cont = (b & (1 << 7)) > 0;
        }
        return bout.toByteArray();
    }

    public void encode(byte[] value, BitChannel channel, Resolver resolver) throws IOException {
        channel.write(value, 0, value.length - 1);
    }

    public Expression<Integer, Resolver> getSize() {
        return null;
    }

    public CodecDescriptor getCodecDescriptor() {
        return new CodecDescriptor() {

            public <C extends ParaContents<?>> Documenter<C> summary() {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.document(reference(Adjective.A, true));
                        target.text(".");
                    }
                };
            }

            public <C extends ParaContents<?>> Documenter<C> reference(final Adjective adjective, final boolean startWithCapital) {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.text(adjective.asTextPreferA(startWithCapital))
                                .text("variable length encoded byte array.");
                    }
                };
            }

            public <C extends SimpleContents<?>> Documenter<C> details(String bufferReference) {
                return new Documenter<C>() {
                    public void document(C target) {
                        target.para()
                                .text("The number of bytes is determined by the ")
                                .text("leading bit of the individual bytes; ")
                                .text("if the first bit of a byte is 1, then ")
                                .text("more bytes are expted to follow.");
                    }
                };

            }

            public boolean requiresDedicatedSection() {
                return false;
            }

            public String getTitle() {
                assert requiresDedicatedSection();
                return null;
            }
        };
    }

    public Class<?>[] getTypes() {
        return new Class<?>[] { Byte[].class };
    }

    public Class<?> getType() {
        return Byte[].class;
    }
}

VariableLengthByteArrayCodecFactory:

package org.codehaus.preon.sample.varlength;

import java.lang.reflect.AnnotatedElement;
import org.codehaus.preon.Codec;
import org.codehaus.preon.CodecFactory;
import org.codehaus.preon.ResolverContext;

public class VariableLengthByteArrayCodecFactory implements CodecFactory {

    public <T> Codec<T> create(AnnotatedElement metadata, Class<T> type, ResolverContext context) {
        if (metadata != null && metadata.isAnnotationPresent(VarLengthEncoded.class) && type == byte[].class) {
            return (Codec<T>) new VariableLengthByteArrayCodec();
        } else {
            return null;
        }
    }

}

VarLengthEncoded:

package org.codehaus.preon.sample.varlength;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface VarLengthEncoded {
}

И, наконец, вот как вы его используете:

public static class SomeHolder {

    @VarLengthEncoded byte[] value;

    public byte[] getValue() {
        return value;
    }

}

...
Codec<SomeHolder> codec = Codecs.create(SomeHolder.class, new VariableLengthByteArrayCodecFactory());
SomeHolder holder = Codecs.decode(codec, (byte) 0xff, (byte) 0x0f);
assertThat(holder.getValue(), is(not(nullValue())));
assertThat(holder.getValue().length, is(2));
assertThat(holder.getValue()[0], is((byte) 0xff));
assertThat(holder.getValue()[1], is((byte) 0x0f));

Это может показаться довольно большим количеством кода, но если вы внимательно проверите, то заметите, что большая часть кода на самом деле следит за тем, чтобы генерировалось правильное описание всякий раз, когда вы создаете документацию для классов с аннотацией @VarLengthEncoded. Если вас вообще не волнует документация, вы можете просто вернуть CodecDescriptor по умолчанию.

Итак, я предполагаю, что суть этого ответа такова: безусловно, есть случаи, когда предоставление реализации в самом Preon перегружало бы фреймворк. Это не означает, что вы должны полагаться на все, что фреймворк предлагает по умолчанию. Это просто означает, что вы должны подключить свои собственные расширения.

person Wilfred Springer    schedule 13.02.2011
comment
Я не ожидал отточенной реализации кодека, большое спасибо! Я не возражаю против лишних вещей, когда я так легко получаю документацию. - person Dusan; 13.02.2011