binpac: Arrays now suport the &transient attribute.

If set, parsed elements won't actually be added to the array, and read
access to the array aren't permitted. This is helpful to save memory
in the case of large arrays for which elements don't need (or can't)
be buffered.
This commit is contained in:
Robin Sommer 2011-09-22 21:19:16 -07:00 committed by Tim Wojtulewicz
parent 5db7ba4050
commit 092d049f8e

View file

@ -92,6 +92,9 @@ Type *ArrayType::ElementDataType() const
string ArrayType::EvalElement(const string &array, const string &index) const string ArrayType::EvalElement(const string &array, const string &index) const
{ {
if ( attr_transient_ )
throw Exception(this, "cannot access element in &transient array");
return strfmt("(*(%s))[%s]", array.c_str(), index.c_str()); return strfmt("(*(%s))[%s]", array.c_str(), index.c_str());
} }
@ -130,18 +133,18 @@ void ArrayType::ProcessAttr(Attr *a)
{ {
if ( elemtype_->StaticSize(env()) != 1 ) if ( elemtype_->StaticSize(env()) != 1 )
{ {
throw Exception(elemtype_, throw Exception(elemtype_,
"&restofdata can be applied" "&restofdata can be applied"
" to only byte arrays"); " to only byte arrays");
} }
if ( length_ ) if ( length_ )
{ {
throw Exception(length_, throw Exception(length_,
"&restofdata cannot be applied" "&restofdata cannot be applied"
" to arrays with specified length"); " to arrays with specified length");
} }
attr_restofdata_ = true; attr_restofdata_ = true;
// As the array automatically extends to the end of // As the array automatically extends to the end of
// data, we do not have to check boundary. // data, we do not have to check boundary.
SetBoundaryChecked(); SetBoundaryChecked();
} }
@ -158,7 +161,7 @@ void ArrayType::ProcessAttr(Attr *a)
bool ref_input = a->expr()->HasReference(input_macro_id); bool ref_input = a->expr()->HasReference(input_macro_id);
if ( ref_element && ref_input ) if ( ref_element && ref_input )
{ {
throw Exception(a->expr(), throw Exception(a->expr(),
"cannot reference both $element and $input " "cannot reference both $element and $input "
"in the same &until---please separate them."); "in the same &until---please separate them.");
} }
@ -167,7 +170,7 @@ void ArrayType::ProcessAttr(Attr *a)
{ {
if ( attr_until_element_expr_ ) if ( attr_until_element_expr_ )
{ {
throw Exception(a->expr(), throw Exception(a->expr(),
"multiple &until on $element"); "multiple &until on $element");
} }
attr_until_element_expr_ = a->expr(); attr_until_element_expr_ = a->expr();
@ -176,7 +179,7 @@ void ArrayType::ProcessAttr(Attr *a)
{ {
if ( attr_until_input_expr_ ) if ( attr_until_input_expr_ )
{ {
throw Exception(a->expr(), throw Exception(a->expr(),
"multiple &until on $input"); "multiple &until on $input");
} }
attr_until_input_expr_ = a->expr(); attr_until_input_expr_ = a->expr();
@ -185,7 +188,7 @@ void ArrayType::ProcessAttr(Attr *a)
{ {
if ( attr_generic_until_expr_ ) if ( attr_generic_until_expr_ )
{ {
throw Exception(a->expr(), throw Exception(a->expr(),
"multiple &until condition"); "multiple &until condition");
} }
attr_generic_until_expr_ = a->expr(); attr_generic_until_expr_ = a->expr();
@ -206,15 +209,15 @@ void ArrayType::Prepare(Env *env, int flags)
ID *elem_var = new ID(fmt("%s__elem", value_var()->Name())); ID *elem_var = new ID(fmt("%s__elem", value_var()->Name()));
ID *elem_it_var = new ID(fmt("%s__it", elem_var->Name())); ID *elem_it_var = new ID(fmt("%s__it", elem_var->Name()));
elem_var_field_ = elem_var_field_ =
new ParseVarField(Field::CLASS_MEMBER, elem_var, elemtype_); new ParseVarField(Field::CLASS_MEMBER, elem_var, elemtype_);
AddField(elem_var_field_); AddField(elem_var_field_);
if ( incremental_parsing() ) if ( incremental_parsing() )
{ {
arraylength_var_field_ = arraylength_var_field_ =
new PrivVarField(arraylength_var, extern_type_int->Clone()); new PrivVarField(arraylength_var, extern_type_int->Clone());
elem_it_var_field_ = elem_it_var_field_ =
new PrivVarField(elem_it_var, extern_type_int->Clone()); new PrivVarField(elem_it_var, extern_type_int->Clone());
AddField(arraylength_var_field_); AddField(arraylength_var_field_);
@ -222,25 +225,25 @@ void ArrayType::Prepare(Env *env, int flags)
} }
else else
{ {
arraylength_var_field_ = arraylength_var_field_ =
new TempVarField(arraylength_var, extern_type_int->Clone()); new TempVarField(arraylength_var, extern_type_int->Clone());
elem_it_var_field_ = elem_it_var_field_ =
new TempVarField(elem_it_var, extern_type_int->Clone()); new TempVarField(elem_it_var, extern_type_int->Clone());
arraylength_var_field_->Prepare(env); arraylength_var_field_->Prepare(env);
elem_it_var_field_->Prepare(env); elem_it_var_field_->Prepare(env);
// Add elem_dataptr_var only when not parsing incrementally // Add elem_dataptr_var only when not parsing incrementally
ID *elem_dataptr_var = ID *elem_dataptr_var =
new ID(fmt("%s__dataptr", elem_var->Name())); new ID(fmt("%s__dataptr", elem_var->Name()));
elem_dataptr_var_field_ = new TempVarField( elem_dataptr_var_field_ = new TempVarField(
elem_dataptr_var, elem_dataptr_var,
extern_type_const_byteptr->Clone()); extern_type_const_byteptr->Clone());
elem_dataptr_var_field_->Prepare(env); elem_dataptr_var_field_->Prepare(env);
// until(dataptr >= end_of_data) // until(dataptr >= end_of_data)
elem_dataptr_until_expr_ = new Expr( elem_dataptr_until_expr_ = new Expr(
Expr::EXPR_GE, Expr::EXPR_GE,
new Expr(elem_dataptr_var->clone()), new Expr(elem_dataptr_var->clone()),
new Expr(end_of_data->clone())); new Expr(end_of_data->clone()));
} }
@ -269,8 +272,8 @@ void ArrayType::GenArrayLength(Output *out_cc, Env *env, const DataPtr& data)
if ( length_ ) if ( length_ )
{ {
out_cc->println("%s = %s;", out_cc->println("%s = %s;",
env->LValue(arraylength_var()), env->LValue(arraylength_var()),
length_->EvalExpr(out_cc, env)); length_->EvalExpr(out_cc, env));
env->SetEvaluated(arraylength_var()); env->SetEvaluated(arraylength_var());
@ -285,22 +288,22 @@ void ArrayType::GenArrayLength(Output *out_cc, Env *env, const DataPtr& data)
env->LValue(arraylength_var())); env->LValue(arraylength_var()));
out_cc->println("}"); out_cc->println("}");
out_cc->dec_indent(); out_cc->dec_indent();
// Check negative array length // Check negative array length
out_cc->println("if ( %s < 0 )", out_cc->println("if ( %s < 0 )",
env->LValue(arraylength_var())); env->LValue(arraylength_var()));
out_cc->inc_indent(); out_cc->inc_indent();
out_cc->println("{"); out_cc->println("{");
out_cc->println("%s = 0;", out_cc->println("%s = 0;",
env->LValue(arraylength_var())); env->LValue(arraylength_var()));
out_cc->println("}"); out_cc->println("}");
out_cc->dec_indent(); out_cc->dec_indent();
} }
else if ( attr_restofdata_ ) else if ( attr_restofdata_ )
{ {
ASSERT(elemtype_->StaticSize(env) == 1); ASSERT(elemtype_->StaticSize(env) == 1);
out_cc->println("%s = (%s) - (%s);", out_cc->println("%s = (%s) - (%s);",
env->LValue(arraylength_var()), env->LValue(arraylength_var()),
env->RValue(end_of_data), env->RValue(end_of_data),
data.ptr_expr()); data.ptr_expr());
env->SetEvaluated(arraylength_var()); env->SetEvaluated(arraylength_var());
@ -313,6 +316,9 @@ void ArrayType::GenPubDecls(Output *out_h, Env *env)
if ( declared_as_type() ) if ( declared_as_type() )
{ {
if ( attr_transient_ )
throw Exception(this, "cannot access element in &transient array");
out_h->println("int size() const { return %s ? %s->size() : 0; }", out_h->println("int size() const { return %s ? %s->size() : 0; }",
env->RValue(value_var()), env->RValue(value_var()),
env->RValue(value_var())); env->RValue(value_var()));
@ -339,7 +345,7 @@ void ArrayType::GenInitCode(Output *out_cc, Env *env)
Type::GenInitCode(out_cc, env); Type::GenInitCode(out_cc, env);
if ( incremental_parsing() ) if ( incremental_parsing() )
{ {
out_cc->println("%s = -1;", out_cc->println("%s = -1;",
env->LValue(elem_it_var())); env->LValue(elem_it_var()));
} }
} }
@ -352,10 +358,10 @@ void ArrayType::GenCleanUpCode(Output *out_cc, Env *env)
if ( ! elem_var_field_ ) if ( ! elem_var_field_ )
{ {
ID *elem_var = new ID(fmt("%s__elem", value_var()->Name())); ID *elem_var = new ID(fmt("%s__elem", value_var()->Name()));
elem_var_field_ = elem_var_field_ =
new ParseVarField( new ParseVarField(
Field::NOT_CLASS_MEMBER, Field::NOT_CLASS_MEMBER,
elem_var, elem_var,
elemtype_); elemtype_);
elem_var_field_->Prepare(env); elem_var_field_->Prepare(env);
} }
@ -368,9 +374,9 @@ void ArrayType::GenCleanUpCode(Output *out_cc, Env *env)
env->RValue(value_var())); env->RValue(value_var()));
out_cc->inc_indent(); out_cc->inc_indent();
out_cc->println("{"); out_cc->println("{");
out_cc->println("%s %s = (*%s)[i];", out_cc->println("%s %s = (*%s)[i];",
elemtype_->DataTypeStr().c_str(), elemtype_->DataTypeStr().c_str(),
env->LValue(elem_var()), env->LValue(elem_var()),
lvalue()); lvalue());
elemtype_->GenCleanUpCode(out_cc, env); elemtype_->GenCleanUpCode(out_cc, env);
out_cc->println("}"); out_cc->println("}");
@ -389,7 +395,7 @@ string ArrayType::GenArrayInit(Output *out_cc, Env *env, bool known_array_length
array_str = lvalue(); array_str = lvalue();
if ( incremental_parsing() ) if ( incremental_parsing() )
{ {
out_cc->println("if ( %s < 0 )", out_cc->println("if ( %s < 0 )",
env->LValue(elem_it_var())); env->LValue(elem_it_var()));
out_cc->inc_indent(); out_cc->inc_indent();
out_cc->println("{"); out_cc->println("{");
@ -397,12 +403,12 @@ string ArrayType::GenArrayInit(Output *out_cc, Env *env, bool known_array_length
out_cc->println("%s = 0;", env->LValue(elem_it_var())); out_cc->println("%s = 0;", env->LValue(elem_it_var()));
} }
out_cc->println("%s = new %s;", out_cc->println("%s = new %s;",
lvalue(), vector_str_.c_str()); lvalue(), vector_str_.c_str());
if ( known_array_length ) if ( known_array_length )
{ {
out_cc->println("%s->reserve(%s);", out_cc->println("%s->reserve(%s);",
lvalue(), env->RValue(arraylength_var())); lvalue(), env->RValue(arraylength_var()));
} }
@ -418,23 +424,30 @@ string ArrayType::GenArrayInit(Output *out_cc, Env *env, bool known_array_length
void ArrayType::GenElementAssignment(Output *out_cc, Env *env, void ArrayType::GenElementAssignment(Output *out_cc, Env *env,
string const &array_str, bool use_vector) string const &array_str, bool use_vector)
{ {
if ( attr_transient_ )
{
// Just discard.
out_cc->println("delete %s;", env->LValue(elem_var()));
return;
}
// Assign the element // Assign the element
if ( ! use_vector ) if ( ! use_vector )
{ {
out_cc->println("%s[%s] = %s;", out_cc->println("%s[%s] = %s;",
array_str.c_str(), array_str.c_str(),
env->LValue(elem_it_var()), env->LValue(elem_it_var()),
env->LValue(elem_var())); env->LValue(elem_var()));
} }
else else
{ {
out_cc->println("%s->push_back(%s);", out_cc->println("%s->push_back(%s);",
array_str.c_str(), array_str.c_str(),
env->LValue(elem_var())); env->LValue(elem_var()));
} }
} }
void ArrayType::DoGenParseCode(Output *out_cc, Env *env, void ArrayType::DoGenParseCode(Output *out_cc, Env *env,
const DataPtr& data, int flags) const DataPtr& data, int flags)
{ {
GenArrayLength(out_cc, env, data); GenArrayLength(out_cc, env, data);
@ -466,10 +479,10 @@ void ArrayType::DoGenParseCode(Output *out_cc, Env *env,
{ {
// Do not compute size_var on incremental input // Do not compute size_var on incremental input
compute_size_var = false; compute_size_var = false;
if ( ! incremental_parsing() && if ( ! incremental_parsing() &&
( StaticSize(env) >= 0 || ( StaticSize(env) >= 0 ||
( env->Evaluated(arraylength_var()) && ( env->Evaluated(arraylength_var()) &&
elemtype_->StaticSize(env) >= 0 ) ) ) elemtype_->StaticSize(env) >= 0 ) ) )
{ {
GenBoundaryCheck(out_cc, env, data); GenBoundaryCheck(out_cc, env, data);
@ -491,7 +504,7 @@ void ArrayType::DoGenParseCode(Output *out_cc, Env *env,
if ( elem_dataptr_var() ) if ( elem_dataptr_var() )
{ {
out_cc->println("const_byteptr %s = %s;", out_cc->println("const_byteptr %s = %s;",
env->LValue(elem_dataptr_var()), data.ptr_expr()); env->LValue(elem_dataptr_var()), data.ptr_expr());
env->SetEvaluated(elem_dataptr_var()); env->SetEvaluated(elem_dataptr_var());
@ -499,13 +512,13 @@ void ArrayType::DoGenParseCode(Output *out_cc, Env *env,
} }
string for_condition = known_array_length ? string for_condition = known_array_length ?
strfmt("%s < %s", strfmt("%s < %s",
env->LValue(elem_it_var()), env->LValue(elem_it_var()),
env->RValue(arraylength_var())) : env->RValue(arraylength_var())) :
"/* forever */"; "/* forever */";
out_cc->println("for (; %s; ++%s)", out_cc->println("for (; %s; ++%s)",
for_condition.c_str(), for_condition.c_str(),
env->LValue(elem_it_var())); env->LValue(elem_it_var()));
out_cc->inc_indent(); out_cc->inc_indent();
out_cc->println("{"); out_cc->println("{");
@ -515,13 +528,13 @@ void ArrayType::DoGenParseCode(Output *out_cc, Env *env,
if ( elem_dataptr_var() ) if ( elem_dataptr_var() )
GenUntilCheck(out_cc, env, elem_dataptr_until_expr_, false); GenUntilCheck(out_cc, env, elem_dataptr_until_expr_, false);
elemtype_->GenPreParsing(out_cc, env); elemtype_->GenPreParsing(out_cc, env);
elemtype_->GenParseCode(out_cc, env, elem_data, flags); elemtype_->GenParseCode(out_cc, env, elem_data, flags);
if ( incremental_parsing() ) if ( incremental_parsing() )
{ {
out_cc->println("if ( ! %s )", out_cc->println("if ( ! %s )",
elemtype_->parsing_complete(env).c_str()); elemtype_->parsing_complete(env).c_str());
out_cc->inc_indent(); out_cc->inc_indent();
out_cc->println("goto %s;", kNeedMoreData); out_cc->println("goto %s;", kNeedMoreData);
@ -532,11 +545,11 @@ void ArrayType::DoGenParseCode(Output *out_cc, Env *env,
if ( elem_dataptr_var() ) if ( elem_dataptr_var() )
{ {
out_cc->println("%s += %s;", out_cc->println("%s += %s;",
env->LValue(elem_dataptr_var()), env->LValue(elem_dataptr_var()),
elemtype_->DataSize(0, env, elem_data).c_str()); elemtype_->DataSize(0, env, elem_data).c_str());
out_cc->println("BINPAC_ASSERT(%s <= %s);", out_cc->println("BINPAC_ASSERT(%s <= %s);",
env->RValue(elem_dataptr_var()), env->RValue(elem_dataptr_var()),
env->RValue(end_of_data)); env->RValue(end_of_data));
} }
@ -556,7 +569,7 @@ void ArrayType::DoGenParseCode(Output *out_cc, Env *env,
if ( compute_size_var && elem_dataptr_var() && ! env->Evaluated(size_var()) ) if ( compute_size_var && elem_dataptr_var() && ! env->Evaluated(size_var()) )
{ {
// Compute the data size // Compute the data size
out_cc->println("%s = %s - (%s);", out_cc->println("%s = %s - (%s);",
env->LValue(size_var()), env->LValue(size_var()),
env->RValue(elem_dataptr_var()), env->RValue(elem_dataptr_var()),
data.ptr_expr()); data.ptr_expr());
@ -572,7 +585,7 @@ void ArrayType::GenUntilInputCheck(Output *out_cc, Env *env)
elem_input_var_id, extern_type_const_bytestring->Clone()); elem_input_var_id, extern_type_const_bytestring->Clone());
elem_input_var_field_->Prepare(env); elem_input_var_field_->Prepare(env);
out_cc->println("%s %s(%s, %s);", out_cc->println("%s %s(%s, %s);",
extern_type_const_bytestring->DataTypeStr().c_str(), extern_type_const_bytestring->DataTypeStr().c_str(),
env->LValue(elem_input_var()), env->LValue(elem_input_var()),
env->RValue(begin_of_data), env->RValue(begin_of_data),
@ -582,22 +595,22 @@ void ArrayType::GenUntilInputCheck(Output *out_cc, Env *env)
GenUntilCheck(out_cc, env, attr_until_input_expr_, true); GenUntilCheck(out_cc, env, attr_until_input_expr_, true);
} }
void ArrayType::GenUntilCheck(Output *out_cc, Env *env, void ArrayType::GenUntilCheck(Output *out_cc, Env *env,
Expr *until_expr, bool delete_elem) Expr *until_expr, bool delete_elem)
{ {
ASSERT(until_expr); ASSERT(until_expr);
Env check_env(env, this); Env check_env(env, this);
check_env.AddMacro(element_macro_id, check_env.AddMacro(element_macro_id,
new Expr(elem_var()->clone())); new Expr(elem_var()->clone()));
if ( elem_input_var() ) if ( elem_input_var() )
{ {
check_env.AddMacro(input_macro_id, check_env.AddMacro(input_macro_id,
new Expr(elem_input_var()->clone())); new Expr(elem_input_var()->clone()));
} }
out_cc->println("// Check &until(%s)", until_expr->orig()); out_cc->println("// Check &until(%s)", until_expr->orig());
out_cc->println("if ( %s )", out_cc->println("if ( %s )",
until_expr->EvalExpr(out_cc, &check_env)); until_expr->EvalExpr(out_cc, &check_env));
out_cc->inc_indent(); out_cc->inc_indent();
out_cc->println("{"); out_cc->println("{");
@ -624,12 +637,12 @@ void ArrayType::GenDynamicSize(Output *out_cc, Env *env,
const DataPtr& data) const DataPtr& data)
{ {
ASSERT(! incremental_input()); ASSERT(! incremental_input());
DEBUG_MSG("Generating dynamic size for array `%s'\n", DEBUG_MSG("Generating dynamic size for array `%s'\n",
value_var()->Name()); value_var()->Name());
int elem_w = elemtype_->StaticSize(env); int elem_w = elemtype_->StaticSize(env);
if ( elem_w >= 0 && if ( elem_w >= 0 &&
! attr_until_element_expr_ && ! attr_until_element_expr_ &&
! attr_until_input_expr_ && ! attr_until_input_expr_ &&
( length_ || attr_restofdata_ ) ) ( length_ || attr_restofdata_ ) )
{ {
@ -661,7 +674,7 @@ int ArrayType::StaticSize(Env *env) const
if ( elem_w < 0 ) if ( elem_w < 0 )
return -1; return -1;
DEBUG_MSG("static size of %s:%s = %d * %d\n", DEBUG_MSG("static size of %s:%s = %d * %d\n",
decl_id()->Name(), lvalue(), elem_w, num); decl_id()->Name(), lvalue(), elem_w, num);
return num * elem_w; return num * elem_w;
@ -675,18 +688,18 @@ void ArrayType::SetBoundaryChecked()
void ArrayType::DoMarkIncrementalInput() void ArrayType::DoMarkIncrementalInput()
{ {
elemtype_->MarkIncrementalInput(); elemtype_->MarkIncrementalInput();
} }
bool ArrayType::RequiresAnalyzerContext() bool ArrayType::RequiresAnalyzerContext()
{ {
return Type::RequiresAnalyzerContext() || return Type::RequiresAnalyzerContext() ||
( length_ && length_->RequiresAnalyzerContext() ) || ( length_ && length_->RequiresAnalyzerContext() ) ||
elemtype_->RequiresAnalyzerContext(); elemtype_->RequiresAnalyzerContext();
} }
bool ArrayType::DoTraverse(DataDepVisitor *visitor) bool ArrayType::DoTraverse(DataDepVisitor *visitor)
{ {
if ( ! Type::DoTraverse(visitor) ) if ( ! Type::DoTraverse(visitor) )
return false; return false;
@ -694,7 +707,7 @@ bool ArrayType::DoTraverse(DataDepVisitor *visitor)
return false; return false;
if ( ! elemtype_->Traverse(visitor) ) if ( ! elemtype_->Traverse(visitor) )
return false; return false;
return true; return true;
} }