cdm/src/main/java/ucar/nc2/stream/NcStreamDataRow.java
NcStreamProto.ArrayStructureRow.Builder encodeStructureData(String structName, Array data) {
    assert data instanceof ArrayStructure;
    ArrayStructure as = (ArrayStructure) data;
    int nelems = (int) as.getSize();

    List<MemberData> memberData = new ArrayList<>();
    StructureMembers sm = as.getStructureMembers();
    for (StructureMembers.Member m : sm.getMembers()) {
      memberData.add( new MemberData(m, nelems));
    }

    // use most efficient form of data extraction
    for (int recno=0; recno<nelems; recno++) {
      for (MemberData md : memberData) {
        if (md.member.isVariableLength()) {
          md.vlenList.add(as.getArray(recno, md.member));
        } else {
          extractData(as, recno, md);
        }
      }
    }

    NcStreamProto.ArrayStructureRow.Builder builder = NcStreamProto.ArrayStructureRow.newBuilder();
    for (MemberData md : memberData) {
      NcStreamProto.Member.Builder member = NcStreamProto.Member.newBuilder();
      member.setShortName(md.member.getName());
      member.setDataType(NcStream.convertDataType(md.member.getDataType()));
      /* LOOK
      member.setNelems(md.nelems);
      if (md.member.isVariableLength()) {
        md.completeVlens();
        nested.addAllVlens (md.vlens);
        nested.setPrimdata(ByteString.copyFrom(md.bb));

      } else if (md.member.getDataType() == DataType.STRING)
        nested.addAllStringdata(md.stringList);
      else if (md.member.getDataType() == DataType.OPAQUE)
        nested.addAllOpaquedata(md.opaqueList);
      else
        nested.setPrimdata(ByteString.copyFrom(md.bb)); */

      builder.addMembers(member);
    }

    return builder;
  }
NcStreamProto.ArrayStructureRow.Builder encodeStructureData(String structName, Array data) {
    assert data instanceof ArrayStructure;
    ArrayStructure as = (ArrayStructure) data;
    int nelems = (int) as.getSize();

    List<MemberData> memberData = new ArrayList<>();
    StructureMembers sm = as.getStructureMembers();
    for (StructureMembers.Member m : sm.getMembers()) {
      memberData.add( new MemberData(m, nelems));
    }

    // use most efficient form of data extraction
    for (int recno=0; recno<nelems; recno++) {
      for (MemberData md : memberData) {
        if (md.member.isVariableLength()) {
          md.vlenList.add(as.getArray(recno, md.member));
        } else {
          extractData(as, recno, md);
        }
      }
    }

    NcStreamProto.ArrayStructureRow.Builder builder = NcStreamProto.ArrayStructureRow.newBuilder();
    for (MemberData md : memberData) {
      NcStreamProto.Member.Builder member = NcStreamProto.Member.newBuilder();
      member.setShortName(md.member.getName());
      member.setDataType(NcStream.convertDataType(md.member.getDataType()));
      /* LOOK
      member.setNelems(md.nelems);
      if (md.member.isVariableLength()) {
        md.completeVlens();
        nested.addAllVlens (md.vlens);
        nested.setPrimdata(ByteString.copyFrom(md.bb));

      } else if (md.member.getDataType() == DataType.STRING)
        nested.addAllStringdata(md.stringList);
      else if (md.member.getDataType() == DataType.OPAQUE)
        nested.addAllOpaquedata(md.opaqueList);
      else
        nested.setPrimdata(ByteString.copyFrom(md.bb)); */

      builder.addMembers(member);
    }

    return builder;
  }
/*
message Member {
string shortName = 1;
DataType dataType = 2;
repeated uint32 shape = 3;  // or section?
bool isVlen = 4;
}

message ArrayStructureRow {
repeated Member members = 1;
uint64 nrows = 5;      // number of rows in this message
uint32 rowLength = 6;  // length in bytes of each row

bytes fixdata = 10;            // fixed data
repeated string stringdata = 11;  // string dataType
repeated bytes bytedata = 13;  // opaque dataType and vlens
repeated ArrayStructureRow structdata = 14;  // structure/seq dataType
}