2011-02-10 75 views
0

如何解析一個可變長度的字節序列,其中第一位(BigEndian)指示使用Preon是否跟隨另一個字節?可變長度序列,第一位指示序列結束,preon

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); 
    } 

    //... 
} 

出於某種原因,例如上面提到的,遞歸方法前子將只解析條目(父母和子女)的2個實例,即使有應該是3

謝謝。

回答

2

爲此,我建議實施您自己的Codec或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本身提供實現肯定會使框架過載。這並不意味着你應該依靠框架默認提供的所有東西。這意味着你應該插入你自己的擴展。

+0

我並不期待編解碼器的拋光實現,非常感謝!當我獲得簡單的文檔時,我不介意額外的東西。 – Dusan 2011-02-13 14:41:00