2022-12-06 19:16:13 +03:00
|
|
|
|
|
|
|
{% macro decl_units(meta) %}
|
|
|
|
|
|
|
|
{%- for u in meta.getunits ~%}
|
|
|
|
{%- if u.object is instanceof('\\mtgMetaStruct') -%}
|
2022-12-07 15:39:44 +03:00
|
|
|
{{ _self.decl_struct(u.object) }}
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- elseif u.object is instanceof('\\mtgMetaEnum') -%}
|
|
|
|
{{ _self.decl_enum(u.object) }}
|
2022-12-07 12:54:56 +03:00
|
|
|
{%- elseif u.object is instanceof('\\mtgMetaRPC') -%}
|
2022-12-07 15:39:44 +03:00
|
|
|
{{ _self.decl_rpc(u.object) }}
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- endif ~%}
|
|
|
|
{%- endfor ~%}
|
|
|
|
|
|
|
|
{% endmacro %}
|
|
|
|
|
2022-12-07 15:39:44 +03:00
|
|
|
{% macro decl_struct(o, extra = '') %}
|
2022-12-07 11:49:34 +03:00
|
|
|
{%- if o.parent and has_token(o, 'POD') -%}
|
|
|
|
{{Error("@POD structs can't have a parent: " ~ o.name)}}
|
|
|
|
{%- endif -%}
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- if o.parent and has_token(o, 'bitfields') -%}
|
|
|
|
{{Error("@bitfields structs can't have a parent: " ~ o.name)}}
|
|
|
|
{%- endif -%}
|
|
|
|
|
|
|
|
{{_self.attributes(o)}}
|
2022-12-07 15:01:28 +03:00
|
|
|
public {{_self.struct_type(o)}} {{o.name}} {{_self.base_struct_class(o)}}
|
2022-12-06 19:16:13 +03:00
|
|
|
{
|
|
|
|
{{_self.decl_struct_fields(o)}}
|
|
|
|
|
|
|
|
public {{o.parent ? 'new'}} const uint STATIC_CLASS_ID = {{o.classid}};
|
|
|
|
|
|
|
|
public {{_self.override(o)}} uint CLASS_ID()
|
|
|
|
{
|
|
|
|
return {{o.classid}};
|
|
|
|
}
|
|
|
|
|
|
|
|
{{_self.comment_POD_begin(o)}}
|
|
|
|
public {{o.name}}()
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
{{_self.comment_POD_end(o)}}
|
|
|
|
|
|
|
|
public {{_self.override(o)}} void reset()
|
|
|
|
{
|
|
|
|
{{_self.struct_fields_reset(o)}}
|
|
|
|
}
|
|
|
|
|
|
|
|
{{_self.comment_non_cloneable_begin(o)}}
|
|
|
|
public {{_self.virtual_clone(o)}} void copy(IMetaStruct other)
|
|
|
|
{
|
|
|
|
copyFrom(({{o.name}})other);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void copyFrom({{o.name}} other)
|
|
|
|
{
|
|
|
|
var ctx = Meta.PrepareForClone(ref other);
|
|
|
|
ctx.factory = AutogenBundle.createById;
|
|
|
|
ctx.reader.BeginContainer();
|
|
|
|
reset();
|
|
|
|
syncFields(ctx);
|
|
|
|
ctx.reader.EndContainer();
|
2022-12-07 15:01:28 +03:00
|
|
|
{%- if has_token(o, 'bitfields') ~%}
|
|
|
|
fields_mask = other.fields_mask;
|
|
|
|
{%- endif ~%}
|
2022-12-06 19:16:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public {{_self.virtual_clone(o)}} IMetaStruct clone()
|
|
|
|
{
|
|
|
|
var copy = new {{o.name}}();
|
|
|
|
copy.copy(this);
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
{{_self.comment_non_cloneable_end(o)}}
|
|
|
|
|
|
|
|
public {{_self.override(o)}} void syncFields(MetaSyncContext ctx)
|
|
|
|
{
|
2022-12-07 11:49:34 +03:00
|
|
|
{{_self.sync_fields(o)}}
|
2022-12-06 19:16:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public {{_self.override(o)}} int getFieldsCount()
|
|
|
|
{
|
2022-12-07 11:49:34 +03:00
|
|
|
return {{fields_count(o)}};
|
2022-12-06 19:16:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public {{_self.override(o)}} int getWritableFieldsCount()
|
|
|
|
{
|
2022-12-07 12:54:56 +03:00
|
|
|
{%~ if has_token(o, 'bitfields') ~%}
|
2022-12-07 11:49:34 +03:00
|
|
|
return Meta.GetDirtyFieldsCount(fields_mask, fields_count: {{fields_count(o)}})
|
|
|
|
+ Meta.MASK_HEADER_FIELDS_COUNT;
|
2022-12-07 12:54:56 +03:00
|
|
|
{% else %}
|
2022-12-07 11:49:34 +03:00
|
|
|
return {{fields_count(o)}};
|
|
|
|
{%- endif ~%}
|
2022-12-06 19:16:13 +03:00
|
|
|
}
|
2022-12-07 11:49:34 +03:00
|
|
|
|
|
|
|
{%- if has_token(o, 'bitfields') ~%}
|
|
|
|
{{_self.bitmask_helpers(o)}}
|
|
|
|
{%- endif -%}
|
2022-12-07 15:01:28 +03:00
|
|
|
|
|
|
|
{%- if has_token(o, 'bhl_bind') ~%}
|
|
|
|
{{_self.get_itype(o)}}
|
|
|
|
{%- endif -%}
|
2022-12-06 19:16:13 +03:00
|
|
|
|
2022-12-07 15:32:29 +03:00
|
|
|
{%- if has_token(o, 'id') ~%}
|
|
|
|
public ulong getPrimaryId()
|
|
|
|
{
|
|
|
|
return (ulong){{token(o, 'id')}};
|
|
|
|
}
|
|
|
|
{%- endif -%}
|
|
|
|
|
|
|
|
{%- if has_token(o, 'diffable') ~%}
|
2022-12-07 15:39:44 +03:00
|
|
|
{{_self.diffable_support(o)}}
|
2022-12-07 15:32:29 +03:00
|
|
|
{%- endif -%}
|
|
|
|
|
2022-12-06 19:16:13 +03:00
|
|
|
{{extra}}
|
|
|
|
}
|
|
|
|
|
|
|
|
{% endmacro %}
|
|
|
|
|
|
|
|
{%- macro decl_struct_fields(o) -%}
|
|
|
|
{%- if has_token(o, 'bitfields') ~%}
|
|
|
|
public long fields_mask;
|
|
|
|
{%- endif -%}
|
|
|
|
{%- for f in o.fields ~%}
|
|
|
|
{{_self.decl_struct_field(o, f)}}
|
|
|
|
{%- endfor -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
|
|
|
{%- macro decl_struct_field(o, f) -%}
|
|
|
|
{{_self.attributes(f)}}
|
2022-12-07 15:01:28 +03:00
|
|
|
public {{f.type|cs_type}} {{f.name}} {% if not has_token(o, 'POD') -%} {{_self.decl_init_value(f.type)}} {%- endif -%};
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- endmacro -%}
|
|
|
|
|
|
|
|
{% macro decl_init_value(type) %}
|
|
|
|
{%- if type is instanceof('\\mtgBuiltinType') -%}
|
|
|
|
{%- if type.isstring -%} = ""{%- endif -%}
|
|
|
|
{%- else -%}
|
|
|
|
= new {{type|cs_type}}()
|
|
|
|
{%- endif -%}
|
|
|
|
{% endmacro %}
|
|
|
|
|
|
|
|
{% macro struct_fields_reset(o) %}
|
|
|
|
{%- if o.parent ~%}
|
|
|
|
base.reset();
|
|
|
|
{%- endif -%}
|
|
|
|
{%- for f in o.fields ~%}
|
|
|
|
{{var_reset(f.name, f.type, token_or(f, 'default', null))}}
|
|
|
|
{%- endfor -%}
|
|
|
|
{%- if has_token(o, 'bitfields') ~%}
|
|
|
|
fields_mask = 0L;
|
|
|
|
{%- endif -%}
|
|
|
|
{% endmacro %}
|
|
|
|
|
|
|
|
{%- macro attributes(o) %}
|
|
|
|
{%- if has_token(o, 'cs_attributes') -%}
|
|
|
|
{%- for attr in token(o, 'cs_attributes')|split(',') -%}
|
|
|
|
[{{attr}}]
|
|
|
|
{%- endfor -%}
|
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro %}
|
|
|
|
|
|
|
|
{%- macro struct_type(o) -%}
|
|
|
|
{{has_token(o, 'POD') ? 'struct' : 'class'}}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
2022-12-07 15:01:28 +03:00
|
|
|
{%- macro base_struct_class(o) -%}
|
|
|
|
{%- if has_token(o, 'POD') -%}
|
|
|
|
: IMetaStruct
|
|
|
|
{%- else -%}
|
|
|
|
: {{o.parent ? o.parent.name : 'BaseMetaStruct'}}
|
|
|
|
{{- has_token(o, 'cloneable') and not o.parent ? ', IMetaCloneable' -}}
|
|
|
|
{{- has_token(o, 'bhl_bind') and not o.parent ? ', bhl.ITyped' -}}
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
|
|
|
{%- macro override(o) -%}
|
|
|
|
{%- if not has_token(o, 'POD') -%}
|
|
|
|
override
|
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
2022-12-07 15:01:28 +03:00
|
|
|
{%- macro virtual(o) -%}
|
|
|
|
{%- if not has_token(o, 'POD') -%}
|
|
|
|
{{o.parent ? 'override' : 'virtual'}}
|
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- macro comment_POD_begin(o) -%}
|
|
|
|
{%- if has_token(o, 'POD') -%}
|
2022-12-07 11:49:34 +03:00
|
|
|
/* commented in POD
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
|
|
|
{%- macro comment_POD_end(o) -%}
|
|
|
|
{%- if has_token(o, 'POD') -%}
|
|
|
|
*/
|
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
|
|
|
{%- macro comment_non_cloneable_begin(o) -%}
|
|
|
|
{%- if not has_token(o, 'cloneable') -%}
|
2022-12-07 11:49:34 +03:00
|
|
|
/* commented in non-cloneable
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
|
|
|
{%- macro comment_non_cloneable_end(o) -%}
|
|
|
|
{%- if not has_token(o, 'cloneable') -%}
|
|
|
|
*/
|
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
|
|
|
{%- macro virtual_clone(o) -%}
|
2022-12-07 15:01:28 +03:00
|
|
|
{%- if has_token(o, 'cloneable') -%}
|
|
|
|
{%- if has_token(o, 'POD') -%}
|
|
|
|
{%- elseif o.parent and has_token_in_parent(o.parent, 'cloneable') -%}
|
|
|
|
override
|
|
|
|
{%- else -%}
|
|
|
|
virtual
|
|
|
|
{%- endif -%}
|
2022-12-06 19:16:13 +03:00
|
|
|
{%- endif -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
2022-12-07 11:49:34 +03:00
|
|
|
{%- macro sync_fields(o) -%}
|
|
|
|
{%- if has_token(o, 'bitfields') ~%}
|
|
|
|
var bitctx = new Meta.BitfieldsContext(ctx, fields_mask);
|
|
|
|
bitctx.SyncMaskHeader();
|
|
|
|
{%- endif -%}
|
|
|
|
{%- if o.parent ~%}
|
|
|
|
base.syncFields(ctx);
|
|
|
|
{%- endif -%}
|
|
|
|
{%- for f in o.fields ~%}
|
|
|
|
{{var_sync(f.name, f.type, 'ctx', f.tokens, get_sync_opts(o, 'bitctx'))}}
|
|
|
|
{%- endfor -%}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
|
|
|
{%- macro bitmask_helpers(o) ~%}
|
|
|
|
|
|
|
|
public void ResetFieldMask()
|
|
|
|
{
|
|
|
|
fields_mask = 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SetPrimaryFieldsChanged()
|
|
|
|
{
|
|
|
|
{%- for f in o.fields ~%}
|
|
|
|
{%- if is_primary_field(o, f) ~%}
|
|
|
|
Meta.SetFieldDirty(ref fields_mask, {{loop.index0}});
|
|
|
|
{%- endif -%}
|
|
|
|
{%- endfor ~%}
|
|
|
|
}
|
|
|
|
|
2022-12-07 12:54:56 +03:00
|
|
|
public void SetDirtyMask()
|
|
|
|
{
|
|
|
|
fields_mask = ~0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SetDirtyMaskDeep()
|
|
|
|
{
|
|
|
|
SetDirtyMask();
|
|
|
|
{%- for f in o.fields ~%}
|
|
|
|
{%- if f.type is instanceof('\\mtgMetaStruct') and has_token(f.type, 'bitfields') ~%}
|
|
|
|
{{f.name}}.SetDirtyMaskDeep();
|
|
|
|
{%- elseif f.type is instanceof('\\mtgArrType') and f.type.value is instanceof('\\mtgMetaStruct') and has_token(f.type.value, "bitfields") ~%}
|
|
|
|
{
|
|
|
|
for(int i=0;i<{{f.name}}.Count;++i)
|
|
|
|
{
|
|
|
|
var __tmp = {{f.name}}[i];
|
|
|
|
__tmp.SetDirtyMaskDeep();
|
|
|
|
{{f.name}}[i] = __tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{%- endif -%}
|
|
|
|
{%- endfor ~%}
|
|
|
|
}
|
|
|
|
|
2022-12-07 11:49:34 +03:00
|
|
|
{%- endmacro -%}
|
|
|
|
|
2022-12-07 15:01:28 +03:00
|
|
|
|
|
|
|
{% macro get_itype(o) %}
|
|
|
|
|
|
|
|
public {{_self.virtual(o)}} bhl.IType GetIType()
|
|
|
|
{
|
|
|
|
return bhl.BHL_Types.Type_{{o.name}};
|
|
|
|
}
|
|
|
|
{%- endmacro -%}
|
|
|
|
|
2022-12-06 19:16:13 +03:00
|
|
|
{% macro decl_enum(o) %}
|
2022-12-07 12:54:56 +03:00
|
|
|
{{_self.attributes(o)}}
|
|
|
|
public enum {{o.name}}
|
|
|
|
{
|
|
|
|
{%- for n,v in o.values ~%}
|
|
|
|
{{n}} = {{v}},
|
|
|
|
{%- endfor ~%}
|
|
|
|
}
|
2022-12-06 19:16:13 +03:00
|
|
|
{% endmacro %}
|
|
|
|
|
2022-12-07 15:39:44 +03:00
|
|
|
{% macro decl_rpc(o) %}
|
2022-12-07 12:54:56 +03:00
|
|
|
|
2022-12-07 15:39:44 +03:00
|
|
|
{{_self.decl_struct(o.req)}}
|
|
|
|
{{_self.decl_struct(o.rsp)}}
|
2022-12-07 12:54:56 +03:00
|
|
|
|
|
|
|
public class {{o.name}} : IRpc
|
|
|
|
{
|
|
|
|
public IRpcError error = null;
|
|
|
|
public {{o.req.name}} req = new {{o.req.name}}();
|
|
|
|
public {{o.rsp.name}} rsp = new {{o.rsp.name}}();
|
|
|
|
|
|
|
|
public int getCode()
|
|
|
|
{
|
|
|
|
return {{o.code}};
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setError(IRpcError error)
|
|
|
|
{
|
|
|
|
this.error = error;
|
|
|
|
}
|
|
|
|
|
|
|
|
public IRpcError getError()
|
|
|
|
{
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
public IMetaStruct getRequest()
|
|
|
|
{
|
|
|
|
return req as IMetaStruct;
|
|
|
|
}
|
|
|
|
|
|
|
|
public IMetaStruct getResponse()
|
|
|
|
{
|
|
|
|
return rsp as IMetaStruct;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{% endmacro %}
|
2022-12-07 15:32:29 +03:00
|
|
|
|
2022-12-07 15:39:44 +03:00
|
|
|
{%- macro diffable_support(o) -%}
|
2022-12-07 15:32:29 +03:00
|
|
|
|
2022-12-07 15:39:44 +03:00
|
|
|
{% for u in get_diff_related_units(o) %}
|
2022-12-07 15:32:29 +03:00
|
|
|
{{_self.diff_methods(u)}}
|
|
|
|
{% endfor %}
|
|
|
|
|
|
|
|
static public List<ulong> GetClassRemovedIds(List<DataClassIds> removed_ids, uint class_id)
|
|
|
|
{
|
|
|
|
if(removed_ids == null)
|
|
|
|
return null;
|
|
|
|
DataClassIds res;
|
|
|
|
if(!FindClassRemovedIds(removed_ids, class_id, out res))
|
|
|
|
{
|
|
|
|
res = new DataClassIds();
|
|
|
|
res.class_id = class_id;
|
|
|
|
res.ids = new List<ulong>();
|
|
|
|
removed_ids.Add(res);
|
|
|
|
}
|
|
|
|
return res.ids;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool FindClassRemovedIds(List<DataClassIds> removed_ids, uint class_id, out DataClassIds res)
|
|
|
|
{
|
|
|
|
for(int i=0;i<removed_ids.Count;++i)
|
|
|
|
{
|
|
|
|
if(removed_ids[i].class_id == class_id)
|
|
|
|
{
|
|
|
|
res = removed_ids[i];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res = default(DataClassIds);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
{% endmacro %}
|
|
|
|
|
|
|
|
{%- macro diff_methods(u) -%}
|
|
|
|
|
|
|
|
static public bool DiffOne(ref {{u.name}} curr, {{u.name}} old)
|
|
|
|
{
|
|
|
|
return !{{_self.compare_func_name(u)}}(ref curr, old);
|
|
|
|
}
|
|
|
|
|
|
|
|
static public bool DiffCollection(List<{{u.name}}> items, List<{{u.name}}> old_items, List<{{u.name}}> changed, List<DataClassIds> removed)
|
|
|
|
{
|
|
|
|
bool has_diff = false;
|
|
|
|
if(changed != null)
|
|
|
|
changed.Clear();
|
|
|
|
|
|
|
|
SortByPrimaryId(ref items);
|
|
|
|
SortByPrimaryId(ref old_items);
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
int old_i = 0;
|
|
|
|
for(; i < items.Count && old_i < old_items.Count;)
|
|
|
|
{
|
|
|
|
var old = old_items[old_i];
|
|
|
|
var item = items[i];
|
|
|
|
|
|
|
|
if(old.getPrimaryId() < item.getPrimaryId())
|
|
|
|
{
|
|
|
|
var removed_ids = GetClassRemovedIds(removed, {{u.name}}.STATIC_CLASS_ID);
|
|
|
|
if(removed_ids != null)
|
|
|
|
removed_ids.Add(old.getPrimaryId());
|
|
|
|
old_i++;
|
|
|
|
has_diff = true;
|
|
|
|
}
|
|
|
|
else if(old.getPrimaryId() > item.getPrimaryId())
|
|
|
|
{
|
|
|
|
item.SetDirtyMask();
|
|
|
|
if(changed != null)
|
|
|
|
changed.Add(item);
|
|
|
|
i++;
|
|
|
|
has_diff = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(!{{_self.compare_func_name(u)}}(ref item, old))
|
|
|
|
{
|
|
|
|
if(changed != null)
|
|
|
|
changed.Add(item);
|
|
|
|
has_diff = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
old_i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//items leftovers are considered changed
|
|
|
|
for(; i < items.Count; i++)
|
|
|
|
{
|
|
|
|
var item = items[i];
|
|
|
|
item.SetDirtyMask();
|
|
|
|
if(changed != null)
|
|
|
|
changed.Add(item);
|
|
|
|
has_diff = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//old items leftovers are considered removed
|
|
|
|
for(; old_i < old_items.Count; old_i++)
|
|
|
|
{
|
|
|
|
var removed_ids = GetClassRemovedIds(removed, {{u.name}}.STATIC_CLASS_ID);
|
|
|
|
if(removed_ids != null)
|
|
|
|
removed_ids.Add(old_items[old_i].getPrimaryId());
|
|
|
|
has_diff = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//if(changed.Count > 0)
|
|
|
|
// Meta.LogDebug("Changed in collection {{u.name}}");
|
|
|
|
//if(removed_ids.Count > 0)
|
|
|
|
// Meta.LogDebug("Removed in collection {{u.name}}");
|
|
|
|
|
|
|
|
return has_diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
class {{u.name}}Compare : IComparer<{{u.name}}>
|
|
|
|
{
|
|
|
|
public int Compare({{u.name}} o1, {{u.name}} o2)
|
|
|
|
{
|
|
|
|
var id1 = o1.getPrimaryId();
|
|
|
|
var id2 = o2.getPrimaryId();
|
|
|
|
if(id1 == id2)
|
|
|
|
return 0;
|
|
|
|
return id1 > id2 ? 1 : -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static {{u.name}}Compare {{u.name}}_compare = new {{u.name}}Compare();
|
|
|
|
|
|
|
|
static void SortByPrimaryId(ref List<{{u.name}}> list)
|
|
|
|
{
|
|
|
|
list.Sort({{u.name}}_compare);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static bool {{_self.compare_func_name(u)}}(ref {{u.name}} a, {{u.name}} b)
|
|
|
|
{
|
|
|
|
bool is_equal = true;
|
|
|
|
|
|
|
|
$compare_fn_str
|
|
|
|
|
|
|
|
return is_equal;
|
|
|
|
}
|
|
|
|
|
|
|
|
{% endmacro %}
|
|
|
|
|
|
|
|
{% macro compare_func_name(o) %}
|
|
|
|
{{has_token(u, 'bitfields')?'CompareAndMarkFields':'IsEqual'}}
|
|
|
|
{% endmacro %}
|