def add_buffer_size(): size_expr = size_to_bytes(compute_buffer_size(type, original_type), type) return Expr(copy_pred(argument)).if_then_else( type.lifetime.equals("AVA_CALL").if_then_else( f"{size} += command_channel_buffer_size(__chan, {size_expr});\n", f"{size} += ava_shadow_buffer_size(&__ava_endpoint, __chan, {size_expr});\n" ), type.lifetime.equals("AVA_CALL").if_then_else( "", f"{size} += ava_shadow_buffer_size_without_data(&__ava_endpoint, __chan, {size_expr});\n" ))
def get_buffer( target: str, target_type: Type, value: str, type_: Type, *, original_type: Optional[Type], precomputed_size=None, not_null=False, declare_buffer_size=True, ) -> Expr: """ Generate code that sets target to the local buffer to use for value. After this code executes `__buffer_size` is equal to the buffer size in elements. :param target: The variable in which to store the buffer pointer. :param value: The original value to translate. :param type_: The type of value. :param original_type: The original type if type is immediately inside a ConditionalType. :param precomputed_size: The expression for a precomputed size of value in *elements*. :param not_null: If True, then this function does not generate NULL checks. :return: A series of C statements. """ size_expr = compute_buffer_size( type_, original_type) if precomputed_size is None else precomputed_size declarations = DECLARE_BUFFER_SIZE_EXPR if declare_buffer_size else Expr( "") pointee_size = f"sizeof({type_.pointee.spelling})" # for void* buffer, assume that each element is 1 byte if type_.pointee.is_void: pointee_size = "1" return declarations.then(( type_.lifetime.not_equals("AVA_CALL") & (Expr(value).not_equals("NULL") | not_null) ).if_then_else( # FIXME: The initial assert is probably incorrect. Should it be checking the ADDRESSES? # Should it be there at all? Commented for now: AVA_DEBUG_ASSERT({target} != {value}); # (was at top of code) # FIXME: Should we check the buffer size? Can it be computed here? # AVA_DEBUG_ASSERT(__buffer_size == {size_expr}); (was at bottom of code) f""" /* Size is in bytes until the division below. */ {target} = ({target_type})({get_buffer_expr(value, type_, not_null=not_null, size_out="__buffer_size")}); AVA_DEBUG_ASSERT(__buffer_size % {pointee_size} == 0); __buffer_size /= {pointee_size}; /* Size is now in elements. */ """.lstrip(), f""" __buffer_size = {size_expr}; {target} = ({target_type})({get_buffer_expr(value, type_, not_null=not_null, size_out="__buffer_size")}); """.strip(), ))
def attach_buffer(target, value, data, type: Type, copy: Union[Expr, Any], *, cmd, original_type: Optional[Type], expect_reply: bool, precomputed_size=None): """ Generate code to attach a buffer to a command. :param target: The value to set to the buffer offset after attaching. :param value: The value to attach. :param type: The type of value. :param copy: An expression which is true if if this value should be copied. :param cmd: The command to attach to. :param original_type: The original type if type is immediately inside a ConditionalType. :param precomputed_size: The expression for a precomputed size of value in *elements*. :return: A series of C statements. """ cmd = f"(struct command_base*){cmd}" size_expr = size_to_bytes( compute_buffer_size(type, original_type) if precomputed_size is None else precomputed_size, type) def simple_attach(func): return lambda: f"""{target} = ({type.nonconst.spelling}){func}(__chan, {cmd}, {data}, {size_expr}); """ def zerocopy_attach(func="ava_zcopy_region_encode_position_independent"): return lambda: f"""{target} = ({type.nonconst.spelling}){func}(__ava_endpoint.zcopy_region, {data}); """ def shadow_attach(func): return lambda: f"""{target} = ({type.nonconst.spelling}){func}(&__ava_endpoint, __chan, {cmd}, {value}, {data}, {size_expr}, {type.lifetime}, {type.buffer_allocator}, {type.buffer_deallocator}, alloca(sizeof(struct ava_buffer_header_t)));""" return type.transfer.equals("NW_ZEROCOPY_BUFFER").if_then_else( zerocopy_attach(), type.lifetime.equals("AVA_CALL").if_then_else( Expr(copy).if_then_else( simple_attach("command_channel_attach_buffer"), f"{target} = HAS_OUT_BUFFER_SENTINEL;" if expect_reply else f"{target} = NULL; /* No output */\n"), Expr(copy).if_then_else( shadow_attach("ava_shadow_buffer_attach_buffer"), shadow_attach("ava_shadow_buffer_attach_buffer_without_data"), )))