package org.yamcs.parameterarchive; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.yamcs.parameter.Value; import org.yamcs.protobuf.Yamcs.Value.Type; import org.yamcs.utils.DecodingException; import org.yamcs.utils.ValueUtility; import org.yamcs.utils.VarIntUtil; import com.google.protobuf.InvalidProtocolBufferException; /** * GenericValueSegment keeps an ArrayList of Values. * It is used during the archive buildup and as a catch all for non optimized ValueSegments. * * * */ public class GenericValueSegment extends BaseSegment implements ValueSegment { List<Value> values = new ArrayList<Value>(); public GenericValueSegment() { super(FORMAT_ID_GenericValueSegment); } public void add(int pos, Value v) { values.add(pos, v); } /** * Encode using regular protobuf delimited field writes */ @Override public void writeTo(ByteBuffer bb) { VarIntUtil.writeVarInt32(bb, values.size()); for(Value v: values) { byte[] b = ValueUtility.toGbp(v).toByteArray(); VarIntUtil.writeVarInt32(bb, b.length); bb.put(b); } } /** * Decode using regular protobuf delimited field writes */ private void parse(ByteBuffer bb) throws DecodingException { int num = VarIntUtil.readVarInt32(bb); for(int i=0;i<num; i++) { int size = VarIntUtil.readVarInt32(bb); byte[] b = new byte[size]; bb.get(b); try { Value v = ValueUtility.fromGpb(org.yamcs.protobuf.Yamcs.Value.parseFrom(b)); values.add(v); } catch (InvalidProtocolBufferException e) { throw new DecodingException("Failed to decode Value: ",e); } } } static GenericValueSegment parseFrom(ByteBuffer bb) throws DecodingException { GenericValueSegment r= new GenericValueSegment(); r.parse(bb); return r; } /** * Transform this generic segment in one of the specialised versions * @return */ public BaseSegment consolidate() { if(values.size()==0) return this; Type type = values.get(0).getType(); switch(type) { case UINT32: case SINT32: case BINARY: case STRING: case FLOAT: case UINT64: case SINT64: case DOUBLE: throw new IllegalStateException("should not be here; specific segments shall be used for this type: "+type); case BOOLEAN: return BooleanValueSegment.consolidate(values); case TIMESTAMP: default: return this; } } @Override public int getMaxSerializedSize() { int size = 4*values.size(); //max 4 bytes for storing each value's size for(Value v: values) { size += ValueUtility.toGbp(v).getSerializedSize(); } return size; } @Override public Value getValue(int index) { return values.get(index); } @Override public Value[] getRange(int posStart, int posStop, boolean ascending) { Value[] r = new Value[posStop-posStart]; if(ascending) { for(int i = posStart; i<posStop; i++) { r[i-posStart] = values.get(i); } } else { for(int i = posStop; i>posStart; i--) { r[posStop-i] = values.get(i); } } return r; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GenericValueSegment other = (GenericValueSegment) obj; if (values == null) { if (other.values != null) return false; else return true; } else { if(other.values==null) return false; } return equal(values, other.values); } private static boolean equal(List<Value> values1, List<Value> values2) { if(values1.size()!=values2.size()) return false; for(int i=0; i<values1.size(); i++) { Value v1 = values1.get(i); Value v2 = values2.get(i); if(!v1.equals(v2)) return false; } return true; } public int size() { return values.size(); } }