последовательность переменной длины, первый бит, обозначающий конец последовательности, преон - PullRequest
0 голосов
/ 10 февраля 2011

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

* ** 1003 тысяча два * Пример
    byte[] bytecode = new byte[] {
            (byte) 0xf2, (byte) 0xbf, (byte) 0xbf, (byte) 0xbf, (byte) 0x50
    };

Примечания

  • первый бит, который указывает следующий, отбрасывается в окончательной полезной нагрузке
  • версия Преона, использованная для этого поста, была 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.

Спасибо.

1 Ответ

2 голосов
/ 13 февраля 2011

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

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

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 перегружало бы фреймворк.Это не значит, что вы должны полагаться на все, что фреймворк может предложить по умолчанию.Это просто означает, что вы должны подключить свои собственные расширения.

...