//  ==========================================================================
//  
//  @(#) $Id: Codec.C,v 1.3 2000/10/13 15:45:51 brian Exp $
//  
//  --------------------------------------------------------------------------
//  
//  Copyright (C) 1997-2000  Brian Bidulock <bidulock@dallas.net>
//  
//  All Rights Reserved.
//  
//  This library is free software; you can redistribute it and/or modify it
//  under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation; either version 2.1 of the License, or (at
//  your option) any later version.
//  
//  This library is distributed in the hope that it will be useful, but
//  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
//  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser Public License
//  for more details.
//  
//  You should have received a copy of the GNU Lesser General Public License
//  along with this library; if not, write to the Free Software Foundation,
//  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
//  Last Modified $Date: 2000/10/13 15:45:51 $ by $Author: brian $
// 
//  --------------------------------------------------------------------------
// 
//  $Log: Codec.C,v $
//  Revision 1.3  2000/10/13 15:45:51  brian
//  First public release.
//
//  ==========================================================================

static char const ident[] = "$Id: Codec.C,v 1.3 2000/10/13 15:45:51 brian Exp $";

#pragma implementation
#include "head.h"

#include "Codec.H"
#include <iostream.h>
#include <stdarg.h>
#include <string.h>

Error::Error(char* d,char* l) {
    desc = d;
    label = l;
};

void
Error::errout() {
    cout.form("ERROR at %s: %s\n",label,desc);
};

Invalid::Invalid(char* l) : Error("Unexpected value."    ,l) { errout(); };
Bounds ::Bounds (char* l) : Error("Out of bounds."       ,l) { errout(); };
Repeats::Repeats(char* l) : Error("Non-integer repeat."  ,l) { errout(); };
SpecErr::SpecErr(char* l) : Error("Specification error." ,l) { errout(); };
NullPtr::NullPtr(char* l) : Error("Null parm pointer."   ,l) { errout(); };

int NewFlag::crumb = 1; // start non-zero

NewFlag::NewFlag() {
    value = 0;
};

NewFlag::NewFlag(bool b) {
    if (b) value = crumb; else value = 0;
};

NewFlag::NewFlag(int i) {
    if (i) value = crumb; else value = 0;
};

NewFlag::operator bool() {
    if (value>crumb) value=0;  // clear really old flags
    return (value==crumb);
};

NewFlag::operator int() {
    if (value>crumb) value=0;  // clear really old flags
    return (value==crumb);
};

void
NewFlag::clearFlags() {
    crumb++; if (!crumb) crumb++;
};

void
FramePointer::set(char* p, int l) {
    pointer = p;
    length = l*8;
    offset = 0;
    size = length;
    end = 0;
};

FramePointer::FramePointer(char* p, int l) {
    pointer = p;
    length = l*8;
    offset = 0;
    size = length;
    end = 0;
};

FramePointer::FramePointer() {
    pointer = 0;
    length = 0;
    offset = 0;
    size = 0;
    end = 0;
};

FramePointer::operator int() {
    return (size+7)>>3;
};

FramePointer::operator char*() {
    return pointer;
};

FramePointer::operator void*() {
    return pointer;
};

char*
FramePointer::point() {
    return pointer + (offset>>3);
};

int Codec::variant = Codec::ansi;
NewFlag Codec::pass2    = false;
NewFlag Codec::pass2req = false;
int Codec::bldseq = 0;

bool
Codec::encode(Codec* c,char* p,int& l) {
    FramePointer f(p,l);
    bldseq = 0; // reset build sequence number
    try { c->encode(f); }
    catch (...) {
        bldseq = 0;
        return false;
    }
    if (pass2req) {
        cout << "Starting pass 2...\n";
        pass2 = true;
        f.offset = 0;
        try { c->encode(f); }
        catch (Error E) {
            E.errout();
            bldseq = 0;
            return false;
        }
    };
    l = (f.end+7)>>3;
    bldseq = 0;
    return true;
};

bool
Codec::decode(Codec* c,char* p,int  l) {
    NewFlag::clearFlags();
    FramePointer f(p,l);
    bldseq = 0; // reset build sequence number
    try { c->decode(f); }
    catch (Error E) {
        E.errout();
        bldseq = 0;
        return false;
    }
    bldseq = 0;
    return true;
};

void
Codec::report(Codec* c) {
    c->report();
    cout.width(78);
    cout.fill('-');
    cout << "-" << '\n';
};

void
Codec::rebuild() {
    NewFlag::clearFlags();
};

Codec::Codec(char* l, char* t, int s, int x) : frame(NULL,0) {
    label   = l;
    title   = t;
    size    = s;
    spare   = x;
    exists  = false;
    built   = false;
    bound   = false;
    dfltval = 0;
    mnem    = NULL;
    text    = NULL;
    master  = true;
    bldord  = 0;
};

Codec::Codec(const Codec& original) {
    label   = original.label;
    title   = original.title;
    size    = original.size;
    spare   = original.spare;
    exists  = false;
    built   = false;
    bound   = false;
    dfltval = original.dfltval;
    mnem    = NULL;
    text    = NULL;
    master  = false;
    bldord  = 0;
};

Codec*
Codec::copy() {
    return new Codec(*this);
};

Codec*
Codec::newcopy(char* l,char *t) {
    return new Codec(l,t,size,spare);
};

Codec::~Codec() {
};

void
Codec::decode(FramePointer& p) {
    frame = p;
    frame.size = size;
    exists = true;
};

void
Codec::encode(FramePointer& p) {
    frame = p;
    frame.size = size;
    if (!(pass2&&exists)) {
        if (built) bldseq = bldord;
        built = false;
        exists = true;
    }
};

void
Codec::bits(int j) {
    int siz, btm, top, bit, i;
    cout.width(3);
    cout.setf(ios::right,ios::adjustfield);
    if ((j==0)&&(frame.offset&0x7)) {
        cout.fill(' ');
        cout << "|" << ' ';
    } else {
        cout.fill('0');
        cout << (frame.offset>>3)+j+1 << ' ';
    }
    siz = frame.size+(j?(frame.offset&0x7):0)-j*8;
    btm = 8-(j?0:frame.offset&0x7);
    top = btm-siz;
    bit = 0x1 << ((top>0?btm-1-top:btm-1)+j*8-(j?(frame.offset&0x7):0));
    for (i=0;i<8;i++) {
        if (i<top) { if (i<top-spare) cout << '.'; else cout << 'x'; continue; }
        if (i>=btm) { cout << '.'; continue; }
        if (value&bit) cout << '1'; else cout << '0';
        bit = bit >> 1;
    }
};

void
Codec::report() {
    bits(0);
    cout << ' ';
    cout.width(22);
    cout.setf(ios::left,ios::adjustfield);
    cout.fill(' ');
    cout << title;
    cout.width(0);
    cout << " : ";
};

void
Codec::morebits() {
    if (frame.size+(frame.offset&0x7)+spare>24) {
        cout.form("--- ******** |          .           :\n");
    } else {
        if (frame.size+(frame.offset&0x7)+spare>8)
        { bits(1); cout << " |          .           :\n"; }
        if (frame.size+(frame.offset&0x7)+spare>16)
        { bits(2); cout << " |          .           :\n"; }
    }
};

void
Codec::build() {
    bldord = bldseq++;
    built = true;
};

bool
Codec::set(int v) {
    value = v;
    bldord = bldseq++;
    built = true;
    bound = true;
    return built;
};

bool
Codec::set(char* m) {
    return false;
};

bool
Codec::set(char* p, int l) {
    frame.set(p,l);
    bldord = bldseq++;
    built = true;
    bound = true;
    return built;
};

bool
Codec::get(int& v) {
    if (exists) v = value;
    return exists;
};

bool
Codec::get(char*& m) {
    return false;
};

bool
Codec::get(char*& p, int& l) {
    if (exists) {
        p = frame.point();
        l = (frame.size+7)/8;
    }
    return exists;
};

bool
Codec::test(int v) {
    if (!exists) return false;
    return value == v;
};

bool
Codec::test(char* m) {
    return false;
};

bool
Codec::test(char* p, int l) {
    if (!exists) return false;
    if (l!=frame.size/8) return false;
    return memcmp(p,frame.point(),l)==0;
};
    


List::List(char* l, char* t, int s, int c, int b,Codec** a) : Codec(l,t,s,0) {
    contents = c;
    branches = b;
    content  = a;
};

List::List(const List& original) : Codec(original) {
    contents = original.contents;
    branches = original.branches;
};

List*
List::newcopy(char* l,char* t) {
    return new List(l,t,size,contents,branches,content);
};

List::~List() {
    int i; for (i=0;i<contents+branches;i++) if (content[i]) delete content[i];
};

void
List::report() {
    if (!exists&&!invalid) return;
    if ((label)&&(title)) {
        cout.width(12);
        cout.fill('-');
        cout.setf(ios::left,ios::adjustfield);
        if (invalid) cout << "---BAD"; else cout << "-";
        cout.width(25);
        cout << label;
        cout.width(41);
        cout << title << '\n';
    }
    int i; for (i=0;i<contents;i++) content[i]->report();
};

void
List::decode(FramePointer& p) {
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    frame = p;
    int i;
    try { for (i=0;i<contents;i++) content[i]->decode(p); }
    catch (...) {
        invalid = true;
        for (;i<contents;i++) content[i]->invalid = true;
        frame.size = p.offset - frame.offset;
        throw;
    }
    frame.size = p.offset - frame.offset;
    exists = true;
};

void
List::encode(FramePointer& p) {
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    frame = p;
    int i; for (i=0;i<contents;i++) content[i]->encode(p);
    frame.size = p.offset - frame.offset;
    if (!(pass2&&exists)) {
        if (built) bldseq = bldord;
        built = false;
        exists = true;
    }
};


Header::Header(char* l, char* t, int s, int c,int b, Codec** a) :
    List(l,t,s,c,b,a) {
};

Content::Content(char* l, char* t, int s, int c,int b, Codec** a) :
    List(l,t,s,c,b,a) {
};

Parameter::Parameter(char* l, char* t, int s, int c, int b, Codec** a) :
    List(l,t,s,c,b,a) {
};

Parameter::Parameter(const Parameter& original) : List(original) {
};

ParameterR::ParameterR(char* l,char* t,int s,int c,int b,Codec** a) :
    Parameter(l,t,s,c,b,a) {
    next = NULL;
};

ParameterR::ParameterR(ParameterR& original) : Parameter(original) {
    next = original.next;
    original.next = this;
};

ParameterR*
ParameterR::copy() {
    return new ParameterR(*this);
};

ParameterR*
ParameterR::newcopy(char* l,char* t) {
    return new ParameterR(l,t,size,contents,branches,content);
};

ParameterR::~ParameterR() {
    if (next) delete next;
};

void
ParameterR::decode(FramePointer& p) {
    if (p.length<p.offset+size) return;
    if (master) { if (next) delete next; next = NULL; }
    frame = p;
    int i;
    try { for (i=0;i<contents;i++) content[i]->decode(p); }
    catch (...) {
        invalid = true;
        for (;i<contents;i++) content[i]->invalid = true;
        frame.size = p.offset - frame.offset;
        throw;
    }
    frame.size = p.offset - frame.offset;
    exists = true;
    ParameterR* temp = this->copy();
    temp->decode(p);
};

void
ParameterR::encode(FramePointer& p) {
    if (p.length<p.offset+size) return;
    if (master) { if (next) delete next; next = NULL; }
    frame = p;
    int i; for (i=0;i<contents;i++) content[i]->encode(p);
    frame.size = p.offset - frame.offset;
    if (!(pass2&&exists)) {
        if (built) bldseq = bldord;
        built = false;
        exists = true;
    }
};

void
ParameterR::report() {
    Parameter::report();
    if (next) next->report();
};

ParameterG::ParameterG(char* l,char* t,...) : Parameter(l,t,0,0,0,NULL) {
    int i;
    va_list argptr;
    va_start(argptr,t);
    while (va_arg(argptr,void*)) contents++;
    va_end  (argptr);
    if (contents) content = new (Codec*)[contents];
    va_start(argptr,t);
    for (i=0;i<contents;i++) {
        content[i] = va_arg(argptr,class Codec*);
        if (content[i]->size==-1) size = -1;
        if ((size!=-1)&&(size<content[i]->size)) size = content[i]->size;
    }
    va_end  (argptr);
};

ParameterG::ParameterG(const ParameterG& original) : Parameter(original) {
    content  = original.content;
    contents = original.contents;
    branches = original.branches;
    int i; for (i=0;i<contents;i++) content[i] = original.content[i];
};

ParameterG::~ParameterG() {
    if (contents) delete[] content;
    contents = 0;
    branches = 0;
};

ParameterP::ParameterP(char* l,char* t,int s,int c,int b,Codec** a):
    Parameter(l,t,s,c,b,a) {
};

void
ParameterP::decode(FramePointer& p) {
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    if (contents<2) { invalid = true; throw SpecErr(label); }
    frame = p;
    frame.size = size;
    FramePointer f(p);
    content[0]->decode(p);                      // this is the pointer
    if (content[0]->value!=0) {
        f.offset += content[0]->value*8;
        int i; for (i=1;i<contents;i++) content[i]->decode(f);
        if (p.end<f.end) p.end = f.end;
    } else {
        content[0]->text = "End of Optional Parameters";
        content[0]->mnem = "EOP";
    }
    exists = true;
};

void
ParameterP::encode(FramePointer& p) {
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    if (contents<2) { invalid = true; throw SpecErr(label); }
    frame = p;
    frame.size = size;
    FramePointer f(p);
    f.offset = ((f.end+7)>>3)*8;                // point octet beyond current encoding
    FramePointer s(f),l(p);
    content[0]->set((f.offset-p.offset)>>3);    // calculate and set offset
    content[0]->encode(p);                      // encode the pointer
    if (pass2) {
        int i; for (i=1;i<contents;i++) content[i]->encode(f); // encode frame
        if (p.end<f.end) p.end = f.end;             // touch up end of frame
        if (f.offset==s.offset) {               // nothing encoded inside
            content[0]->set(0);                 // zero the pointer
            content[0]->encode(l);              // reencode the pointer
            content[0]->text = "End of Optional Parameters";
            content[0]->mnem = "EOP";
        }
    } else {
        pass2req = true;
    }
    if (!(pass2&&exists)) {
        if (built) bldseq = bldord;
        built = false;
        exists = true;
    }
};

ParameterPL::ParameterPL(char* l,char* t,int s,int c,int b,Codec** a) :
    Parameter(l,t,s,c,b,a) {
};

void
ParameterPL::decode(FramePointer& p) {
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    if (contents<3) { invalid = true; throw SpecErr(label); }
    frame = p;
    frame.size = size;
    FramePointer f(p);
    content[0]->decode(p);                      // this is the pointer
    if (content[0]->value==0) { invalid = true; throw NullPtr(label); }
    f.offset += content[0]->value*8;
    content[1]->decode(f);                      // this is the length
    if (f.length<f.offset+content[1]->value*8) { invalid = true; throw Bounds(label); }
    f.length = f.offset+content[1]->value*8;
    int i; for (i=2;i<contents;i++) content[i]->decode(f);
    if (p.end<f.end) p.end = f.end;
    exists = true;
};

void
ParameterPL::encode(FramePointer& p) {
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    if (contents<3) { invalid = true; throw SpecErr(label); }
    frame = p;
    frame.size = size;
    FramePointer f(p);
    f.offset = ((f.end+7)>>3)*8;                // point octet beyond current encoding
    content[0]->set((f.offset-p.offset)>>3);    // calculate and set offset
    content[0]->encode(p);                      // encode the pointer
    if (pass2) {
        FramePointer s(f);                          // get pointer to size indicator for later
        content[1]->encode(f);                      // preencode the length
        int i; for (i=2;i<contents;i++) content[i]->encode(f); // encode frame
        content[1]->set((f.offset-s.offset-8)>>3);  // calculate and set length
        content[1]->encode(s);                      // reencode the length
        if (p.end<f.end) p.end = f.end;             // touch up end of frame
    } else {
        pass2req = true;
    }
    if (!(pass2&&exists)) {
        if (built) bldseq = bldord;
        built = false;
        exists = true;
    }
};

ParameterPNL::ParameterPNL(char* l,char* t,int s,int c,int b,Codec** a) :
    Parameter(l,t,s,c,b,a) {
    next = NULL;
};

ParameterPNL::ParameterPNL(ParameterPNL& original) : Parameter(original) {
    next = original.next;
    original.next = this;
};

ParameterPNL*
ParameterPNL::newcopy(char* l,char* t) {
    return new ParameterPNL(l,t,size,contents,branches,content);
};

ParameterPNL::~ParameterPNL() {
    if (next) delete next;
};

void
ParameterPNL::decode(FramePointer& p) {
    if (master) { if (next) delete next; next = NULL; }
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    if (contents<4) { invalid = true; throw SpecErr(label); }
    frame = p;
    frame.size = size;
    FramePointer f;
    int i;
    f = p;                                  // mark where pointer is
    content[0]->decode(p);                  // this is the pointer
    if (content[0]->value!=0) {             // not end of parameters
        f.offset += content[0]->value*8;    // offset frame
        content[1]->decode(f);              // this is the label
        if (content[1]->value!=0) {         // not end of parameters
            content[2]->decode(f);          // this is the length
            if (f.length<f.offset+content[2]->value*8) { // check length
                content[2]->invalid = true; throw Bounds(content[2]->label);
            }
            f.length = f.offset+content[2]->value*8; // set the length
            try {
                for (i=3;i<contents;i++) content[i]->decode(f); // decode
            } catch (...) { };
            Codec* temp = this->copy();
            temp->decode(p);
        } else {
            content[1]->text = "End of Optional Parameters";
            content[1]->mnem = "EOP";
        }
        if (p.end<f.end) p.end = f.end;
    } else {
        content[0]->text = "End of Optional Parameters";
        content[0]->mnem = "EOP";
    }
    exists = true;
};

void
ParameterPNL::encode(FramePointer& p) {
    if (master) {
        if (next) delete next;
        next = NULL;
    }
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    if (contents<4) { invalid = true; throw SpecErr(label); }
    frame = p;
    frame.size = size;
    FramePointer l,s,f;
    int i,j;
    f = p;                      // mark where pointer is
    f.offset = ((f.end+7)>>3)*8;// point octet beyond current encoding
    l = f;                      // mark where frame starts
    content[0]->set((f.offset-p.offset)>>3); // calculate and set offset
    content[0]->encode(p);      // encode the pointer
    bool found = false;         // check for parameters to encode
    for (j=0;j<((Bra*)content[3])->vals;j++) {
        if ( ((Bra*)content[3])->val[j].path->built ) {
            found = true; break;
        }
    }
    if (found) {
        if (pass2) {
            content[1]->encode(f);  // preencode the label
            s = f;                  // get pointer to size for later
            content[2]->encode(f);  // preencode the length
            for (i=3;i<contents;i++) content[i]->encode(f); // encode
            content[2]->set((f.offset-s.offset-8)>>3); // calc&set length
            content[2]->encode(s);          // reencode the length
            if (p.end<f.end) p.end = f.end; // touch up end of frame
            Codec* temp = this->copy();
            temp->encode(p);
        } else {
            pass2req = true;
            ((Bra*)content[3])->val[j].path->built = false;
            Codec* temp = this->copy();
            temp->encode(p);
            ((Bra*)content[3])->val[j].path->built = true;
        }
    } else {
        if (pass2) {
            content[1]->set(0);     // set for end of parameters
            content[1]->encode(f);  // encode the label
            content[1]->text = "End of Optional Parameters";
            content[1]->mnem = "EOP";
            if (p.end<f.end) p.end = f.end; // touch up end of frame
        } else {
            pass2req = true;
        }
    }
    if (built) bldseq = bldord;
    built = false;
    exists = true;
};

void
ParameterPNL::report() {
    Parameter::report();
    if (next) next->report();
};

ParameterNL::ParameterNL(char* l,char* t,int s,int c,int b,Codec** a) :
    Parameter(l,t,s,c,b,a) {
    next = NULL;
};

ParameterNL::ParameterNL(ParameterNL& original) : Parameter(original) {
    next = original.next;
    original.next = this;
};

ParameterNL*
ParameterNL::newcopy(char* l,char* t) {
    return new ParameterNL(l,t,size,contents,branches,content);
};

ParameterNL::~ParameterNL() {
    if (next) delete next;
};

void
ParameterNL::decode(FramePointer& p) {
    if (master) { if (next) delete next; next = NULL; }
    if (p.length<p.offset+size) return; // assume end of list
    if (contents<3) { invalid = true; throw SpecErr(label); }
    frame = p;
    frame.size = size;
    int i;
    content[0]->decode(p);              // this is the label
    if (content[0]->value!=0) {         // not end of parameters
        content[1]->decode(p);          // this is the length
        if (p.length<p.offset+content[1]->value*8) { // check length
            content[1]->invalid = true; throw Bounds(content[1]->label);
        }
        FramePointer f(p);              // new sized frame
        f.length = f.offset+content[1]->value*8; // set the length
        try {
            for (i=2;i<contents;i++) content[i]->decode(f); // decode
        } catch (...) { };
        frame.size = f.offset - p.offset;
        p.offset = f.length;            // jump past sized frame
        Codec* temp = this->copy();
        temp->decode(p);
    } else {
        content[0]->text = "End of Optional Parameters";
        content[0]->mnem = "EOP";
    }
    exists = true;
};

void
ParameterNL::encode(FramePointer& p) {
    if (master) { if (next) delete next; next = NULL; }
    if (p.length<p.offset+size) { invalid = true; throw Bounds(label); }
    if (contents<3) { invalid = true; throw SpecErr(label); }
    frame = p;
    frame.size = size;
    int i,j;
    bool found = false;         // check for parameters to encode
    for (j=0;j<((Bra*)content[2])->vals;j++) {
        if ( ((Bra*)content[2])->val[j].path->built ) {
            found = true; break;
        }
    }
    if (found) {
        content[0]->encode(p);  // preencode the label
        FramePointer s(p);      // get pointer to size for later
        content[1]->encode(p);  // preencode the length
        for (i=2;i<contents;i++) content[i]->encode(p); // encode
        content[1]->set((p.offset-s.offset-8)>>3); // calc&set length
        content[1]->encode(s);          // reencode the length
        Codec* temp = this->copy();
        temp->encode(p);
    } else {
        if (!master) {
            content[0]->set(0);     // set for end of parameters
            content[0]->encode(p);  // encode the label
            content[0]->text = "End of Optional Parameters";
            content[0]->mnem = "EOP";
        } else {
            return;
        }
    }
    if (p.end<p.offset) p.end = p.offset;
    if (built) bldseq = bldord;
    built = false;
    exists = true;
};

void
ParameterNL::report() {
    Parameter::report();
    if (next) next->report();
};

Field::Field(char* l, char* t, int s, int x) : Codec(l,t,s,x) {
};

void
Field::decode(FramePointer& p) {
    if (p.length<(p.offset+size+spare)) {
        invalid = true;
        cout.form("length = %d, offset = %d, size = %d, spare = %d\n",
            p.length,p.offset,size,spare);
        throw Bounds(label);
    }
    if (!exists) { text = NULL; mnem = NULL; }
    char* ptr = p.point();
    frame = p;
    value = 0;
    value |= ptr[3]; value = value << 8;
    value |= ptr[2]; value = value << 8;
    value |= ptr[1]; value = value << 8;
    value |= ptr[0];
    value = value >> (p.offset&0x7);
    int mask;
    if ((size>0)&&(size<=32)) mask = (0x1<<size)-1; else mask = -1;
    value &= mask;
    frame.size = size;
    p.offset += frame.size+spare; if (p.end < p.offset) p.end = p.offset;
    exists = true;
};

void
Field::encode(FramePointer& p) {
    if (!exists&&!bound) value = dfltval;
    if (p.length<p.offset+size+spare) { invalid = true; throw Bounds(label); }
    if (!exists) { text = NULL; mnem = NULL; }
    char* ptr = p.point();
    frame = p;
    int oldvalue = 0;
    oldvalue |= ptr[3]&0xff; oldvalue = oldvalue << 8;
    oldvalue |= ptr[2]&0xff; oldvalue = oldvalue << 8;
    oldvalue |= ptr[1]&0xff; oldvalue = oldvalue << 8;
    oldvalue |= ptr[0]&0xff;
    int mask;
    if (((size+spare)>0)&&((size+spare)<=32)) mask = (0x1<<(size+spare))-1; else mask = -1;
    oldvalue &= ~(mask << (p.offset&0x7));
    oldvalue |= ((value&mask) << (p.offset&0x7));
    ptr[0] = oldvalue&0xff; oldvalue = oldvalue >> 8;
    ptr[1] = oldvalue&0xff; oldvalue = oldvalue >> 8;
    ptr[2] = oldvalue&0xff; oldvalue = oldvalue >> 8;
    ptr[3] = oldvalue&0xff;
    frame.size = size;
    p.offset += frame.size+spare; if (p.end < p.offset) p.end = p.offset;
    if (!(pass2&&exists)) {
        if (built) bldseq = bldord;
        built = false;
        exists = true;
    }
};

void
Field::report() {
    if (!exists&&!invalid) return;
    Codec::report();
    if (invalid) cout.form("BAD ");
    if (size) cout.form("%d",value);
    if (text) cout.form(" - %s",text);
    cout << '\n';
    Codec::morebits();
};

OctetString::OctetString(char* l, char *t, int s) : Codec(l,t,s,0) {
    dfltval = 0;
};

void
OctetString::decode(FramePointer& p) {
    p.offset = (p.offset+7)&(~0x7);
    if (p.length<p.offset) { invalid = true; throw Bounds(label); }
    frame = p;
    frame.size = p.length - p.offset;
    p.offset += frame.size; if (p.end < p.offset) p.end = p.offset;
    exists = true;
};

void
OctetString::encode(FramePointer& p) {
    p.offset = (p.offset+7)&(~0x7);
    if (p.length<p.offset+frame.size) { invalid = true; throw Bounds(label); }
    char* ptr = p.point();
    if (built) {
        int i; for (i=0;i<(frame.size>>3);i++) ptr[i] = frame.point()[i];
    } else {
        if (size>0) {
            frame.size = size;
            int i; for (i=0;i<(frame.size>>3);i++) ptr[i] = dfltval;
        } else {
            frame.size = 0;
        }
    }
    p.size = frame.size;
    frame = p;
    p.offset += p.size; if (p.end < p.offset) p.end = p.offset;
    if (!(pass2&&exists)) {
        if (built) bldseq = bldord;
        built = false;
        exists = true;
    }
};

void
OctetString::report() {
    if (!exists&&!invalid) return;
    int i,j;
    char* ptr = frame.point();
    int   len = (frame.size+7)/8;
    i=0; j=0;
    value = ptr[0];
    Codec::report();
    if (invalid) cout << "BAD";
    else
    while (i<len) {
        if (j)
            cout.form("--- ******** |           .          : ");
        j++;
        for (;(i<len)&&(i<12*j);i++) {
            cout.width(2);
            cout.fill('0');
            cout.setf(ios::right,ios::adjustfield);
            cout << hex << (unsigned int)ptr[i] << ' ';
        }
    }
    if (!len) cout << "(null)";
    cout << dec;
    cout << '\n';
};


DigitString::DigitString(char* l,char* t,int s) : OctetString(l,t,s) {
    dfltval = 15;
};

void
DigitString::report() {
    static char a[] = "0123456789*?#KS_";
    if (!exists&&!invalid) return;
    int i;
    char* ptr = frame.point();
    int   len = (frame.size+7)/8;
    Codec::report();
    if (invalid) cout << "BAD";
    else
        for (i=0;i<len;i++) {
            cout << a[ptr[i]&0xf];
            cout << a[(ptr[i]>>4)&0xf];
        }
    cout << '\n';
};

AsciiString::AsciiString(char* l,char* t,int s) : OctetString(l,t,s) {
    dfltval = (int)'?';
};

void
AsciiString::report() {
    if (!exists&&!invalid) return;
    int i;
    char* ptr = frame.point();
    int   len = (frame.size+7)/8;
    Codec::report();
    if (invalid) cout << "BAD";
    else
        for (i=0;i<len;i++) {
            cout << ptr[i];
        }
    cout << '\n';
};

Set::Set(char* l, char* t, int s, int x, ...) : Field(l,t,s,x) {
    
    // set up mnemonics and text for various values
    
    va_list argptr;
    int     index;
    char*   mnem;
    char*   text;
    
    vals = 0;
    va_start(argptr,x);
    mnem = va_arg(argptr,char*);
    while (mnem) {
        index = va_arg(argptr,int);
        text  = va_arg(argptr,char*);
        mnem  = va_arg(argptr,char*);
        vals++;
    }
    va_end(argptr);
    
    if (vals) {
        val = new SetStr[vals];
        va_start(argptr,x);
        int i;
        for (i=0;i<vals;i++) {
            val[i].mnem = va_arg(argptr,char*);
            val[i].indx = va_arg(argptr,int);
            val[i].text = va_arg(argptr,char*);
        }
        va_end(argptr);
    } else {
        val = NULL;
    }
};

Set::~Set() {
    if (val) delete[] val;
};

void
Set::decode(FramePointer& p) {
    Field::decode(p);   // just decode as a field
};

void
Set::encode(FramePointer& p) {
    Field::encode(p);   // just encode as a field
};

void
Set::report() {
    if (!exists&&!invalid) return;
    bool found = false;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx == value) || (val[i].indx == -1) ) {
            found = true;
            break;
        }
    }
    Codec::report();
    if (invalid) cout.form("BAD ");
    cout.form("%d",value);
    if (found) cout.form(" - %s",val[i].text);
    cout << '\n';
    Codec::morebits();
};

bool Set::set(int v) { return Codec::set(v); };
bool Set::set(char* p, int l) { return Codec::set(p,l); };

bool
Set::set(char* m) {
    bool found = false;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx!=-1) && (strcmp(m,val[i].mnem)==0) ) {
            found = true;
            break;
        }
    }
    if (found) {
        set(val[i].indx);
    }
    return found;
};

bool Set::get(int& v) { return Codec::get(v); };
bool Set::get(char*& p, int& l) { return Codec::get(p,l); };

bool
Set::get(char*& m) {
    if (!exists&&!invalid) return false;
    bool found = false;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx == value) || (val[i].indx == -1) ) {
            found = true;
            break;
        }
    }
    if (found) {
        m = val[i].mnem;
    }
    return found;
};

bool Set::test(int v) { return Codec::test(v); };
bool Set::test(char* p, int l) { return Codec::test(p,l); };

bool
Set::test(char* m) {
    if (!exists&&!invalid) return false;
    bool found = false;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx!=-1) && (strcmp(m,val[i].mnem)==0) ) {
            found = true;
            break;
        }
    }
    if (found) return (value==val[i].indx);
    return found;
};

Bra::Bra(char* l, char* t, Codec* c, ...) : Codec(l,t,0,0) {
    branch = c;
    
    // set up paths for various values
    
    va_list argptr;
    int     indx;
    Codec*  path;
    
    vals = 0;
    va_start(argptr,c);
    path = va_arg(argptr,Codec*);
    while (path) {
        indx = va_arg(argptr,int);
        path = va_arg(argptr,Codec*);
        vals++;
    }
    va_end(argptr);
    
    if (vals) {
        val = new BraStr[vals];
        va_start(argptr,c);
        int i;
        for (i=0;i<vals;i++) {
            val[i].path = va_arg(argptr,Codec*);
            val[i].indx = va_arg(argptr,int);
        }
        va_end(argptr);
    } else {
        val = NULL;
    }
};

Bra::Bra(const Bra& original) : Codec(original) {
    branch = original.branch;
    vals   = original.vals;
    val    = original.val;
};

Bra::~Bra() {
    if (master) if (val) delete[] val;
};

void
Bra::decode(FramePointer& p) {
    bool found = false;
    frame = p;
    frame.size = size;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx == branch->value) || (val[i].indx == -1) ) {
            found = true;
            break;
        }
    }
    if (found) {
        if (!branch->mnem) branch->mnem = val[i].path->label;
        if (!branch->text) branch->text = val[i].path->title;
        try { val[i].path->decode(p); }
        catch (...) { invalid = true; throw; }
    } else {
        invalid = true;
        branch->invalid = true;
    }
    exists = found;
};

void
Bra::encode(FramePointer& p) {
    bool found = false;
    frame = p;
    frame.size = size;
    int i;
    if (branch->bound) {
        // value was bound and built, find value and encode out
        for (i=0;i<vals;i++) {
            if ( (val[i].indx == branch->value) || (val[i].indx == -1) ) {
                found = true;
                break;
            }
        }
    } else {
        // value not bound, find out which path was built first
        int first;
        int order = MAXINT;
        for (i=0;i<vals;i++) {
            if ( val[i].path->built ) {
                found = true;
                if (val[i].path->bldord<order) {
                    first = i;
                    order = val[i].path->bldord;
                };
            }
        }
        if (found) {
            // rebuild new value :note: this will also bind the value
            i = first;
            branch->set(val[i].indx);
            if (branch->exists) {
                FramePointer t = branch->frame;
                branch->encode(t);
            }
        } else {
            // value not bound, branch not built, encode default path
            for (i=0;i<vals;i++) {
                if ( (val[i].indx == branch->value) || (val[i].indx == -1) ) {
                    found = true;
                    break;
                }
            }
        }
    }
    if (found) {
        if (!branch->mnem) branch->mnem = val[i].path->label;
        if (!branch->text) branch->text = val[i].path->title;
        val[i].path->encode(p);
        if (!(pass2&&exists)) {
            if (built) bldseq = bldord;
            built = false;
            exists = true;
        }
    }
};

void
Bra::report() {
    if (!exists&&!invalid) return;
    bool found = false;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx == branch->value) || (val[i].indx == -1) ) {
            found = true;
            break;
        }
    }
    if (found) {
        val[i].path->report();
        return;
    }
};

bool
Bra::set(char* m) {
    bool found = false;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx!=-1) && (strcmp(m,val[i].path->label)==0) ) {
            found = true;
            break;
        }
    }
    if (found) {
        branch->set(val[i].indx);
    }
    return found;
};

bool
Bra::get(char*& m) {
    if (!exists&&!invalid) return false;
    bool found = false;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx = branch->value) || (val[i].indx == -1) ) {
            found = true;
            break;
        }
    }
    if (found) {
        m = val[i].path->label;
    }
    return found;
};

bool
Bra::test(char* m) {
    if (!exists&&!invalid) return false;
    bool found = false;
    int i;
    for (i=0;i<vals;i++) {
        if ( (val[i].indx!=-1) && (strcmp(m,val[i].path->label)==0) ) {
            found = true;
            break;
        }
    }
    if (found) return (branch->value==val[i].indx);
    return found;
};


