zeek/tools/binpac/src/pac_record.cc
2025-08-20 08:52:24 -07:00

670 lines
18 KiB
C++

#include "pac_record.h"
#include "pac_attr.h"
#include "pac_dataptr.h"
#include "pac_exception.h"
#include "pac_expr.h"
#include "pac_exttype.h"
#include "pac_field.h"
#include "pac_output.h"
#include "pac_type.h"
#include "pac_typedecl.h"
#include "pac_utils.h"
#include "pac_varfield.h"
RecordType::RecordType(RecordFieldList* record_fields) : Type(RECORD)
{
// Here we assume that the type is a standalone type.
value_var_ = nullptr;
// Put all fields in fields_
foreach (i, RecordFieldList, record_fields)
AddField(*i);
// Put RecordField's in record_fields_
record_fields_ = record_fields;
parsing_dataptr_var_field_ = nullptr;
}
RecordType::~RecordType()
{
// Do not delete_list(RecordFieldList, record_fields_)
// because the fields are also in fields_.
delete record_fields_;
delete parsing_dataptr_var_field_;
}
const ID* RecordType::parsing_dataptr_var() const
{
return parsing_dataptr_var_field_ ? parsing_dataptr_var_field_->id() : nullptr;
}
bool RecordType::DefineValueVar() const
{
return false;
}
string RecordType::DataTypeStr() const
{
ASSERT(type_decl());
return strfmt("%s *", type_decl()->class_name().c_str());
}
void RecordType::Prepare(Env* env, int flags)
{
ASSERT(flags & TO_BE_PARSED);
RecordField* prev = nullptr;
int offset = 0;
int seq = 0;
foreach (i, RecordFieldList, record_fields_)
{
RecordField* f = *i;
f->set_record_type(this);
f->set_prev(prev);
if ( prev )
prev->set_next(f);
prev = f;
if ( offset >= 0 )
{
f->set_static_offset(offset);
int w = f->StaticSize(env, offset);
if ( w < 0 )
offset = -1;
else
offset += w;
}
++seq;
f->set_parsing_state_seq(seq);
}
if ( incremental_parsing() )
{
#if 0
ASSERT(! parsing_state_var_field_);
ID *parsing_state_var_id = new ID("parsing_state");
parsing_state_var_field_ = new PrivVarField(
parsing_state_var_id, extern_type_int->Clone());
AddField(parsing_state_var_field_);
ID *parsing_dataptr_var_id = new ID("parsing_dataptr");
parsing_dataptr_var_field_ = new TempVarField(
parsing_dataptr_var_id, extern_type_const_byteptr->Clone());
parsing_dataptr_var_field_->Prepare(env);
#endif
}
Type::Prepare(env, flags);
}
void RecordType::GenPubDecls(Output* out_h, Env* env)
{
Type::GenPubDecls(out_h, env);
}
void RecordType::GenPrivDecls(Output* out_h, Env* env)
{
Type::GenPrivDecls(out_h, env);
}
void RecordType::GenInitCode(Output* out_cc, Env* env)
{
Type::GenInitCode(out_cc, env);
}
void RecordType::GenCleanUpCode(Output* out_cc, Env* env)
{
Type::GenCleanUpCode(out_cc, env);
}
void RecordType::DoGenParseCode(Output* out_cc, Env* env, const DataPtr& data, int flags)
{
if ( ! incremental_input() && StaticSize(env) >= 0 )
GenBoundaryCheck(out_cc, env, data);
if ( incremental_parsing() )
{
out_cc->println("switch ( %s ) {", env->LValue(parsing_state_id));
out_cc->println("case 0:");
out_cc->inc_indent();
foreach (i, RecordFieldList, record_fields_)
{
RecordField* f = *i;
f->GenParseCode(out_cc, env);
out_cc->println("");
}
out_cc->println("");
out_cc->println("%s = true;", env->LValue(parsing_complete_var()));
out_cc->dec_indent();
out_cc->println("}");
}
else
{
ASSERT(data.id() == begin_of_data && data.offset() == 0);
foreach (i, RecordFieldList, record_fields_)
{
RecordField* f = *i;
f->GenParseCode(out_cc, env);
out_cc->println("");
}
if ( incremental_input() )
{
ASSERT(parsing_complete_var());
out_cc->println("%s = true;", env->LValue(parsing_complete_var()));
}
}
if ( ! incremental_input() && AddSizeVar(out_cc, env) )
{
const DataPtr& end_of_record_dataptr = record_fields_->back()->getFieldEnd(out_cc, env);
out_cc->println("%s = %s - %s;", env->LValue(size_var()), end_of_record_dataptr.ptr_expr(),
env->RValue(begin_of_data));
env->SetEvaluated(size_var());
}
if ( ! boundary_checked() )
{
RecordField* last_field = record_fields_->back();
if ( ! last_field->BoundaryChecked() )
GenBoundaryCheck(out_cc, env, data);
}
}
void RecordType::GenDynamicSize(Output* out_cc, Env* env, const DataPtr& data)
{
GenParseCode(out_cc, env, data, 0);
}
int RecordType::StaticSize(Env* env) const
{
int tot_w = 0;
foreach (i, RecordFieldList, record_fields_)
{
RecordField* f = *i;
int w = f->StaticSize(env, tot_w);
if ( w < 0 )
return -1;
tot_w += w;
}
return tot_w;
}
void RecordType::SetBoundaryChecked()
{
Type::SetBoundaryChecked();
if ( StaticSize(env()) < 0 || attr_length_expr_ )
// Don't assume sufficient bounds checking has been done on fields
// if the record is of variable size or if its size is set from &length
// (whose value is not necessarily trustworthy).
return;
foreach (i, RecordFieldList, record_fields_)
{
RecordField* f = *i;
f->SetBoundaryChecked();
}
}
void RecordType::DoMarkIncrementalInput()
{
foreach (i, RecordFieldList, record_fields_)
{
RecordField* f = *i;
f->type()->MarkIncrementalInput();
}
}
bool RecordType::DoTraverse(DataDepVisitor* visitor)
{
return Type::DoTraverse(visitor);
}
bool RecordType::ByteOrderSensitive() const
{
foreach (i, RecordFieldList, record_fields_)
{
RecordField* f = *i;
if ( f->RequiresByteOrder() )
return true;
}
return false;
}
RecordField::RecordField(FieldType tof, ID* id, Type* type)
: Field(tof, TYPE_TO_BE_PARSED | CLASS_MEMBER | PUBLIC_READABLE, id, type)
{
begin_of_field_dataptr = nullptr;
end_of_field_dataptr = nullptr;
field_size_expr = nullptr;
field_offset_expr = nullptr;
end_of_field_dataptr_var = nullptr;
record_type_ = nullptr;
prev_ = nullptr;
next_ = nullptr;
static_offset_ = -1;
parsing_state_seq_ = 0;
boundary_checked_ = false;
}
RecordField::~RecordField()
{
delete begin_of_field_dataptr;
delete end_of_field_dataptr;
delete[] field_size_expr;
delete[] field_offset_expr;
delete end_of_field_dataptr_var;
}
const DataPtr& RecordField::getFieldBegin(Output* out_cc, Env* env)
{
if ( prev() )
return prev()->getFieldEnd(out_cc, env);
else
{
// The first field
if ( ! begin_of_field_dataptr )
{
begin_of_field_dataptr = new DataPtr(env, begin_of_data, 0);
}
return *begin_of_field_dataptr;
}
}
const DataPtr& RecordField::getFieldEnd(Output* out_cc, Env* env)
{
if ( end_of_field_dataptr )
return *end_of_field_dataptr;
const DataPtr& begin_ptr = getFieldBegin(out_cc, env);
if ( record_type()->incremental_parsing() )
{
ASSERT(0);
if ( ! end_of_field_dataptr )
{
const ID* dataptr_var = record_type()->parsing_dataptr_var();
ASSERT(dataptr_var);
end_of_field_dataptr = new DataPtr(env, dataptr_var, 0);
}
}
else
{
int field_offset;
if ( begin_ptr.id() == begin_of_data )
field_offset = begin_ptr.offset();
else
field_offset = -1; // unknown
int field_size = StaticSize(env, field_offset);
if ( field_size >= 0 ) // can be statically determinted
{
end_of_field_dataptr = new DataPtr(env, begin_ptr.id(),
begin_ptr.offset() + field_size);
}
else
{
// If not, we add a variable for the offset after the field
end_of_field_dataptr_var = new ID(strfmt("dataptr_after_%s", id()->Name()));
env->AddID(end_of_field_dataptr_var, TEMP_VAR, extern_type_const_byteptr);
GenFieldEnd(out_cc, env, begin_ptr);
end_of_field_dataptr = new DataPtr(env, end_of_field_dataptr_var, 0);
}
}
return *end_of_field_dataptr;
}
const char* RecordField::FieldSize(Output* out_cc, Env* env)
{
if ( field_size_expr )
return field_size_expr;
const DataPtr& begin = getFieldBegin(out_cc, env);
const DataPtr& end = getFieldEnd(out_cc, env);
if ( begin.id() == end.id() )
field_size_expr = nfmt("%d", end.offset() - begin.offset());
else
field_size_expr = nfmt("(%s - %s)", end.ptr_expr(), begin.ptr_expr());
return field_size_expr;
}
const char* RecordField::FieldOffset(Output* out_cc, Env* env)
{
if ( field_offset_expr )
return field_offset_expr;
const DataPtr& begin = getFieldBegin(out_cc, env);
if ( begin.id() == begin_of_data )
field_offset_expr = nfmt("%d", begin.offset());
else
field_offset_expr = nfmt("(%s - %s)", begin.ptr_expr(), env->RValue(begin_of_data));
return field_offset_expr;
}
// The reasoning behind AttemptBoundaryCheck is: "If my next field
// can check its boundary, then I don't have to check mine, and it
// will save me a boundary-check."
bool RecordField::AttemptBoundaryCheck(Output* out_cc, Env* env)
{
if ( boundary_checked_ )
return true;
// If I do not even know my size till I parse the data, my
// next field won't be able to check its boundary now.
const DataPtr& begin = getFieldBegin(out_cc, env);
if ( StaticSize(env, begin.AbsOffset(begin_of_data)) < 0 )
return false;
// Now we ask the next field to check its boundary.
if ( next() && next()->AttemptBoundaryCheck(out_cc, env) )
{
// If it works, we are all set
SetBoundaryChecked();
return true;
}
else
// If it fails, then I can still try to do it by myself
return GenBoundaryCheck(out_cc, env);
}
RecordDataField::RecordDataField(ID* id, Type* type) : RecordField(RECORD_FIELD, id, type)
{
ASSERT(type_);
}
RecordDataField::~RecordDataField() { }
void RecordDataField::Prepare(Env* env)
{
Field::Prepare(env);
env->SetEvalMethod(id_, this);
env->SetField(id_, this);
}
void RecordDataField::GenParseCode(Output* out_cc, Env* env)
{
if ( env->Evaluated(id()) )
return;
// Always evaluate record fields in order if parsing
// is incremental.
if ( record_type()->incremental_parsing() && prev() )
prev()->GenParseCode(out_cc, env);
DataPtr data(env, nullptr, 0);
if ( ! record_type()->incremental_parsing() )
{
data = getFieldBegin(out_cc, env);
Expr* len_expr = record_type()->attr_length_expr();
int len;
if ( ! record_type()->buffer_input() || (len_expr && len_expr->ConstFold(env, &len)) )
AttemptBoundaryCheck(out_cc, env);
}
out_cc->println("// Parse \"%s\"", id_->Name());
#if 0
out_cc->println("DEBUG_MSG(\"%%.6f Parse %s\\n\", network_time());",
id_->Name());
#endif
type_->GenPreParsing(out_cc, env);
if ( type_->incremental_input() )
{
// The enclosing record type must be incrementally parsed
out_cc->println("%s = %d;", env->LValue(parsing_state_id), parsing_state_seq());
out_cc->println("/* fall through */");
out_cc->dec_indent();
out_cc->println("case %d:", parsing_state_seq());
out_cc->inc_indent();
out_cc->println("{");
}
type_->GenParseCode(out_cc, env, data, 0);
if ( record_type()->incremental_parsing() )
{
ASSERT(type_->incremental_input());
out_cc->println("if ( ! (%s) )", type_->parsing_complete(env).c_str());
out_cc->inc_indent();
out_cc->println("goto %s;", kNeedMoreData);
out_cc->dec_indent();
}
if ( record_type()->incremental_parsing() )
{
#if 0
const ID *dataptr_var =
record_type()->parsing_dataptr_var();
ASSERT(dataptr_var);
out_cc->println("%s += (%s);",
env->LValue(dataptr_var),
type_->DataSize(out_cc, env, data).c_str());
#endif
out_cc->println("}");
}
SetBoundaryChecked();
}
void RecordDataField::GenEval(Output* out_cc, Env* env)
{
GenParseCode(out_cc, env);
}
void RecordDataField::GenFieldEnd(Output* out_cc, Env* env, const DataPtr& field_begin)
{
out_cc->println("const_byteptr const %s = %s + (%s);", env->LValue(end_of_field_dataptr_var),
field_begin.ptr_expr(), type_->DataSize(out_cc, env, field_begin).c_str());
env->SetEvaluated(end_of_field_dataptr_var);
out_cc->println("BINPAC_ASSERT(%s <= %s);", env->RValue(end_of_field_dataptr_var),
env->RValue(end_of_data));
}
void RecordDataField::SetBoundaryChecked()
{
RecordField::SetBoundaryChecked();
type_->SetBoundaryChecked();
}
bool RecordDataField::GenBoundaryCheck(Output* out_cc, Env* env)
{
if ( boundary_checked_ )
return true;
type_->GenBoundaryCheck(out_cc, env, getFieldBegin(out_cc, env));
SetBoundaryChecked();
return true;
}
bool RecordDataField::DoTraverse(DataDepVisitor* visitor)
{
return Field::DoTraverse(visitor);
}
bool RecordDataField::RequiresAnalyzerContext() const
{
return Field::RequiresAnalyzerContext() || type()->RequiresAnalyzerContext();
}
RecordPaddingField::RecordPaddingField(ID* id, PaddingType ptype, Expr* expr)
: RecordField(PADDING_FIELD, id, nullptr), ptype_(ptype), expr_(expr)
{
wordsize_ = -1;
}
RecordPaddingField::~RecordPaddingField() { }
void RecordPaddingField::Prepare(Env* env)
{
Field::Prepare(env);
if ( ptype_ == PAD_TO_NEXT_WORD )
{
if ( ! expr_->ConstFold(env, &wordsize_) )
throw ExceptionPaddingError(this, strfmt("padding word size not a constant"));
}
}
void RecordPaddingField::GenParseCode(Output* out_cc, Env* env)
{
// Always evaluate record fields in order if parsing
// is incremental.
if ( record_type()->incremental_parsing() && prev() )
prev()->GenParseCode(out_cc, env);
}
int RecordPaddingField::StaticSize(Env* env, int offset) const
{
int length;
int target_offset;
int offset_in_word;
switch ( ptype_ )
{
case PAD_BY_LENGTH:
return expr_->ConstFold(env, &length) ? length : -1;
case PAD_TO_OFFSET:
// If the current offset cannot be statically
// determined, we need to Generate code to
// check the offset
if ( offset == -1 )
return -1;
if ( ! expr_->ConstFold(env, &target_offset) )
return -1;
// If both the current and target offsets
// can be statically computed, we can get its
// static size
if ( offset > target_offset )
throw ExceptionPaddingError(this, strfmt("current offset = %d, "
"target offset = %d",
offset, target_offset));
return target_offset - offset;
case PAD_TO_NEXT_WORD:
if ( offset == -1 || wordsize_ == -1 )
return -1;
offset_in_word = offset % wordsize_;
return (offset_in_word == 0) ? 0 : wordsize_ - offset_in_word;
}
return -1;
}
void RecordPaddingField::GenFieldEnd(Output* out_cc, Env* env, const DataPtr& field_begin)
{
ASSERT(! env->Evaluated(end_of_field_dataptr_var));
char* padding_var;
switch ( ptype_ )
{
case PAD_BY_LENGTH:
out_cc->println("if ( (%s) < 0 ) // check for negative pad length",
expr_->EvalExpr(out_cc, env));
out_cc->inc_indent();
out_cc->println("{");
out_cc->println("throw binpac::ExceptionInvalidStringLength(\"%s\", %s);", Location(),
expr_->EvalExpr(out_cc, env));
out_cc->println("}");
out_cc->dec_indent();
out_cc->println("");
out_cc->println("const_byteptr const %s = %s + (%s);",
env->LValue(end_of_field_dataptr_var), field_begin.ptr_expr(),
expr_->EvalExpr(out_cc, env));
out_cc->println("// Checking out-of-bound padding for \"%s\"", field_id_str_.c_str());
out_cc->println("if ( %s > %s || %s < %s )", env->LValue(end_of_field_dataptr_var),
env->RValue(end_of_data), env->LValue(end_of_field_dataptr_var),
field_begin.ptr_expr());
out_cc->inc_indent();
out_cc->println("{");
out_cc->println("throw binpac::ExceptionOutOfBound(\"%s\",", field_id_str_.c_str());
out_cc->println(" (%s), ", expr_->EvalExpr(out_cc, env));
out_cc->println(" (%s) - (%s));", env->RValue(end_of_data),
env->LValue(end_of_field_dataptr_var));
out_cc->println("}");
out_cc->dec_indent();
out_cc->println("");
break;
case PAD_TO_OFFSET:
out_cc->println("const_byteptr %s = %s + (%s);", env->LValue(end_of_field_dataptr_var),
env->RValue(begin_of_data), expr_->EvalExpr(out_cc, env));
out_cc->println("if ( %s < %s )", env->LValue(end_of_field_dataptr_var),
field_begin.ptr_expr());
out_cc->inc_indent();
out_cc->println("{");
out_cc->println("// throw binpac::ExceptionInvalidOffset(\"%s\", %s - %s, %s);",
id_->LocName(), field_begin.ptr_expr(), env->RValue(begin_of_data),
expr_->EvalExpr(out_cc, env));
out_cc->println("%s = %s;", env->LValue(end_of_field_dataptr_var),
field_begin.ptr_expr());
out_cc->println("}");
out_cc->dec_indent();
out_cc->println("if ( %s > %s )", env->LValue(end_of_field_dataptr_var),
env->RValue(end_of_data));
out_cc->inc_indent();
out_cc->println("{");
out_cc->println("throw binpac::ExceptionOutOfBound(\"%s\",", field_id_str_.c_str());
out_cc->println(" (%s), ", expr_->EvalExpr(out_cc, env));
out_cc->println(" (%s) - (%s));", env->RValue(end_of_data),
env->LValue(end_of_field_dataptr_var));
out_cc->println("}");
out_cc->dec_indent();
break;
case PAD_TO_NEXT_WORD:
padding_var = nfmt("%s__size", id()->Name());
out_cc->println("int %s = (%s - %s) %% %d;", padding_var, field_begin.ptr_expr(),
env->RValue(begin_of_data), wordsize_);
out_cc->println("%s = (%s == 0) ? 0 : %d - %s;", padding_var, padding_var, wordsize_,
padding_var);
out_cc->println("const_byteptr const %s = %s + %s;",
env->LValue(end_of_field_dataptr_var), field_begin.ptr_expr(),
padding_var);
delete[] padding_var;
break;
}
env->SetEvaluated(end_of_field_dataptr_var);
}
bool RecordPaddingField::GenBoundaryCheck(Output* out_cc, Env* env)
{
if ( boundary_checked_ )
return true;
const DataPtr& begin = getFieldBegin(out_cc, env);
char* size;
int ss = StaticSize(env, begin.AbsOffset(begin_of_data));
ASSERT(ss >= 0);
size = nfmt("%d", ss);
begin.GenBoundaryCheck(out_cc, env, size, field_id_str_.c_str());
delete[] size;
SetBoundaryChecked();
return true;
}
bool RecordPaddingField::DoTraverse(DataDepVisitor* visitor)
{
return Field::DoTraverse(visitor) && (! expr_ || expr_->Traverse(visitor));
}