diff --git a/hpy/debug/src/autogen_debug_ctx_init.h b/hpy/debug/src/autogen_debug_ctx_init.h index 79768a67e..11f991a14 100644 --- a/hpy/debug/src/autogen_debug_ctx_init.h +++ b/hpy/debug/src/autogen_debug_ctx_init.h @@ -131,6 +131,10 @@ HPyListBuilder debug_ctx_ListBuilder_New(HPyContext *dctx, HPy_ssize_t initial_s void debug_ctx_ListBuilder_Set(HPyContext *dctx, HPyListBuilder builder, HPy_ssize_t index, DHPy h_item); DHPy debug_ctx_ListBuilder_Build(HPyContext *dctx, HPyListBuilder builder); void debug_ctx_ListBuilder_Cancel(HPyContext *dctx, HPyListBuilder builder); +HPyUnicodeBuilder debug_ctx_UnicodeBuilder_New(HPyContext *dctx, HPy_ssize_t size); +int debug_ctx_UnicodeBuilder_Add(HPyContext *dctx, HPyUnicodeBuilder builder, const char *item); +DHPy debug_ctx_UnicodeBuilder_Build(HPyContext *dctx, HPyUnicodeBuilder builder); +void debug_ctx_UnicodeBuilder_Cancel(HPyContext *dctx, HPyUnicodeBuilder builder); HPyTupleBuilder debug_ctx_TupleBuilder_New(HPyContext *dctx, HPy_ssize_t initial_size); void debug_ctx_TupleBuilder_Set(HPyContext *dctx, HPyTupleBuilder builder, HPy_ssize_t index, DHPy h_item); DHPy debug_ctx_TupleBuilder_Build(HPyContext *dctx, HPyTupleBuilder builder); @@ -339,6 +343,10 @@ static inline void debug_ctx_init_fields(HPyContext *dctx, HPyContext *uctx) dctx->ctx_ListBuilder_Set = &debug_ctx_ListBuilder_Set; dctx->ctx_ListBuilder_Build = &debug_ctx_ListBuilder_Build; dctx->ctx_ListBuilder_Cancel = &debug_ctx_ListBuilder_Cancel; + dctx->ctx_UnicodeBuilder_New = &debug_ctx_UnicodeBuilder_New; + dctx->ctx_UnicodeBuilder_Add = &debug_ctx_UnicodeBuilder_Add; + dctx->ctx_UnicodeBuilder_Build = &debug_ctx_UnicodeBuilder_Build; + dctx->ctx_UnicodeBuilder_Cancel = &debug_ctx_UnicodeBuilder_Cancel; dctx->ctx_TupleBuilder_New = &debug_ctx_TupleBuilder_New; dctx->ctx_TupleBuilder_Set = &debug_ctx_TupleBuilder_Set; dctx->ctx_TupleBuilder_Build = &debug_ctx_TupleBuilder_Build; diff --git a/hpy/debug/src/autogen_debug_wrappers.c b/hpy/debug/src/autogen_debug_wrappers.c index 55a42d3d7..486e16295 100644 --- a/hpy/debug/src/autogen_debug_wrappers.c +++ b/hpy/debug/src/autogen_debug_wrappers.c @@ -592,6 +592,26 @@ void debug_ctx_ListBuilder_Cancel(HPyContext *dctx, HPyListBuilder builder) HPyListBuilder_Cancel(get_info(dctx)->uctx, builder); } +HPyUnicodeBuilder debug_ctx_UnicodeBuilder_New(HPyContext *dctx, HPy_ssize_t size) +{ + return HPyUnicodeBuilder_New(get_info(dctx)->uctx, size); +} + +int debug_ctx_UnicodeBuilder_Add(HPyContext *dctx, HPyUnicodeBuilder builder, const char *item) +{ + return HPyUnicodeBuilder_Add(get_info(dctx)->uctx, builder, item); +} + +DHPy debug_ctx_UnicodeBuilder_Build(HPyContext *dctx, HPyUnicodeBuilder builder) +{ + return DHPy_wrap(dctx, HPyUnicodeBuilder_Build(get_info(dctx)->uctx, builder)); +} + +void debug_ctx_UnicodeBuilder_Cancel(HPyContext *dctx, HPyUnicodeBuilder builder) +{ + HPyUnicodeBuilder_Cancel(get_info(dctx)->uctx, builder); +} + HPyTupleBuilder debug_ctx_TupleBuilder_New(HPyContext *dctx, HPy_ssize_t initial_size) { return HPyTupleBuilder_New(get_info(dctx)->uctx, initial_size); diff --git a/hpy/devel/include/common/runtime/ctx_unicodebuilder.h b/hpy/devel/include/common/runtime/ctx_unicodebuilder.h new file mode 100644 index 000000000..5b7924369 --- /dev/null +++ b/hpy/devel/include/common/runtime/ctx_unicodebuilder.h @@ -0,0 +1,15 @@ +#ifndef HPY_COMMON_RUNTIME_UNICODEBUILDER_H +#define HPY_COMMON_RUNTIME_UNICODEBUILDER_H + +#include +#include "hpy.h" +#include "common/hpytype.h" + + +_HPy_HIDDEN HPyUnicodeBuilder ctx_UnicodeBuilder_New(HPyContext *ctx, HPy_ssize_t capacity); +_HPy_HIDDEN int ctx_UnicodeBuilder_Add(HPyContext *ctx, HPyUnicodeBuilder builder, const char *item); +_HPy_HIDDEN HPy ctx_UnicodeBuilder_Build(HPyContext *ctx, HPyUnicodeBuilder builder); +_HPy_HIDDEN void ctx_UnicodeBuilder_Cancel(HPyContext *ctx, HPyUnicodeBuilder builder); + + +#endif /* HPY_COMMON_RUNTIME_UNICODEBUILDER_H */ diff --git a/hpy/devel/include/cpython/hpy.h b/hpy/devel/include/cpython/hpy.h index da30575ca..27e0c6b92 100644 --- a/hpy/devel/include/cpython/hpy.h +++ b/hpy/devel/include/cpython/hpy.h @@ -33,6 +33,7 @@ typedef struct { PyObject *_o; } HPy; typedef struct { Py_ssize_t _lst; } HPyListBuilder; typedef struct { Py_ssize_t _tup; } HPyTupleBuilder; typedef struct { void *_o; } HPyTracker; +typedef struct { void *_o; } HPyUnicodeBuilder; typedef Py_ssize_t HPy_ssize_t; typedef Py_hash_t HPy_hash_t; @@ -280,6 +281,7 @@ HPy_AsPyObject(HPyContext *ctx, HPy h) #include "../common/runtime/ctx_object.h" #include "../common/runtime/ctx_type.h" #include "../common/runtime/ctx_listbuilder.h" +#include "../common/runtime/ctx_unicodebuilder.h" #include "../common/runtime/ctx_tracker.h" #include "../common/runtime/ctx_tuple.h" #include "../common/runtime/ctx_tuplebuilder.h" @@ -433,4 +435,28 @@ HPyTracker_Close(HPyContext *ctx, HPyTracker ht) ctx_Tracker_Close(ctx, ht); } +HPyAPI_FUNC(HPyUnicodeBuilder) +HPyUnicodeBuilder_New(HPyContext *ctx, HPy_ssize_t size) +{ + return ctx_UnicodeBuilder_New(ctx, size); +} + +HPyAPI_FUNC(int) +HPyUnicodeBuilder_Add(HPyContext *ctx, HPyUnicodeBuilder builder, const char *item) +{ + return ctx_UnicodeBuilder_Add(ctx, builder, item); +} + +HPyAPI_FUNC(HPy) +HPyUnicodeBuilder_Build(HPyContext *ctx, HPyUnicodeBuilder builder) +{ + return ctx_UnicodeBuilder_Build(ctx, builder); +} + +HPyAPI_FUNC(void) +HPyUnicodeBuilder_Cancel(HPyContext *ctx, HPyUnicodeBuilder builder) +{ + ctx_UnicodeBuilder_Cancel(ctx, builder); +} + #endif /* !HPy_CPYTHON_H */ diff --git a/hpy/devel/include/universal/autogen_ctx.h b/hpy/devel/include/universal/autogen_ctx.h index 8863cee20..a90643fd7 100644 --- a/hpy/devel/include/universal/autogen_ctx.h +++ b/hpy/devel/include/universal/autogen_ctx.h @@ -210,6 +210,10 @@ struct _HPyContext_s { void (*ctx_ListBuilder_Set)(HPyContext *ctx, HPyListBuilder builder, HPy_ssize_t index, HPy h_item); HPy (*ctx_ListBuilder_Build)(HPyContext *ctx, HPyListBuilder builder); void (*ctx_ListBuilder_Cancel)(HPyContext *ctx, HPyListBuilder builder); + HPyUnicodeBuilder (*ctx_UnicodeBuilder_New)(HPyContext *ctx, HPy_ssize_t size); + int (*ctx_UnicodeBuilder_Add)(HPyContext *ctx, HPyUnicodeBuilder builder, const char *item); + HPy (*ctx_UnicodeBuilder_Build)(HPyContext *ctx, HPyUnicodeBuilder builder); + void (*ctx_UnicodeBuilder_Cancel)(HPyContext *ctx, HPyUnicodeBuilder builder); HPyTupleBuilder (*ctx_TupleBuilder_New)(HPyContext *ctx, HPy_ssize_t initial_size); void (*ctx_TupleBuilder_Set)(HPyContext *ctx, HPyTupleBuilder builder, HPy_ssize_t index, HPy h_item); HPy (*ctx_TupleBuilder_Build)(HPyContext *ctx, HPyTupleBuilder builder); diff --git a/hpy/devel/include/universal/autogen_trampolines.h b/hpy/devel/include/universal/autogen_trampolines.h index 00203a27c..c52b785a5 100644 --- a/hpy/devel/include/universal/autogen_trampolines.h +++ b/hpy/devel/include/universal/autogen_trampolines.h @@ -486,6 +486,22 @@ static inline void HPyListBuilder_Cancel(HPyContext *ctx, HPyListBuilder builder ctx->ctx_ListBuilder_Cancel ( ctx, builder ); } +static inline HPyUnicodeBuilder HPyUnicodeBuilder_New(HPyContext *ctx, HPy_ssize_t size) { + return ctx->ctx_UnicodeBuilder_New ( ctx, size ); +} + +static inline int HPyUnicodeBuilder_Add(HPyContext *ctx, HPyUnicodeBuilder builder, const char *item) { + return ctx->ctx_UnicodeBuilder_Add ( ctx, builder, item ); +} + +static inline HPy HPyUnicodeBuilder_Build(HPyContext *ctx, HPyUnicodeBuilder builder) { + return ctx->ctx_UnicodeBuilder_Build ( ctx, builder ); +} + +static inline void HPyUnicodeBuilder_Cancel(HPyContext *ctx, HPyUnicodeBuilder builder) { + ctx->ctx_UnicodeBuilder_Cancel ( ctx, builder ); +} + static inline HPyTupleBuilder HPyTupleBuilder_New(HPyContext *ctx, HPy_ssize_t initial_size) { return ctx->ctx_TupleBuilder_New ( ctx, initial_size ); } diff --git a/hpy/devel/include/universal/hpy.h b/hpy/devel/include/universal/hpy.h index b401dc240..1ec11ff1e 100644 --- a/hpy/devel/include/universal/hpy.h +++ b/hpy/devel/include/universal/hpy.h @@ -36,6 +36,7 @@ typedef struct _HPy_s { HPy_ssize_t _i; } HPy; typedef struct { HPy_ssize_t _lst; } HPyListBuilder; typedef struct { HPy_ssize_t _tup; } HPyTupleBuilder; typedef struct { HPy_ssize_t _i; } HPyTracker; +typedef struct { HPy_ssize_t _i; } HPyUnicodeBuilder; typedef struct _HPyContext_s HPyContext; diff --git a/hpy/devel/src/runtime/ctx_unicodebuilder.c b/hpy/devel/src/runtime/ctx_unicodebuilder.c new file mode 100644 index 000000000..ea96f3ba7 --- /dev/null +++ b/hpy/devel/src/runtime/ctx_unicodebuilder.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include "hpy.h" + +#ifdef HPY_UNIVERSAL_ABI + // for _h2py and _py2h +# include "handles.h" +#endif + +static const Py_ssize_t HPYUNICODEBUILDER_INITIAL_CAPACITY = 1024; + +typedef struct { + Py_ssize_t capacity; // allocated handles + Py_ssize_t length; + char *buf; +} _PyUnicodeBuilder_s; + +#ifdef HPY_UNIVERSAL_ABI +static inline _PyUnicodeBuilder_s *_hb2pb(HPyUnicodeBuilder ht) { + return (_PyUnicodeBuilder_s *) (ht)._i; +} +static inline HPyUnicodeBuilder _pb2hb(_PyUnicodeBuilder_s *bp) { + return (HPyUnicodeBuilder) {(HPy_ssize_t) (bp)}; +} +#else +static inline _PyUnicodeBuilder_s *_hb2pb(HPyUnicodeBuilder ht) { + return (_PyUnicodeBuilder_s *) (ht)._o; +} +static inline HPyUnicodeBuilder _pb2hb(_PyUnicodeBuilder_s *bp) { + return (HPyUnicodeBuilder) {(void *) (bp)}; +} +#endif + +_HPy_HIDDEN HPyUnicodeBuilder +ctx_UnicodeBuilder_New(HPyContext *ctx, HPy_ssize_t capacity) +{ + _PyUnicodeBuilder_s *bp; + if (capacity == 0) { + // TODO: default value or raise a ValueError? + capacity = HPYUNICODEBUILDER_INITIAL_CAPACITY; + } + capacity++; // always reserve space for the trailing 0 + + bp = malloc(sizeof(_PyUnicodeBuilder_s)); + if (bp == NULL) { + HPyErr_NoMemory(ctx); + return _pb2hb(0); + } + + bp->buf = calloc(1, capacity); + if (bp == NULL) { + free(bp); + HPyErr_NoMemory(ctx); + return _pb2hb(0); + } + + bp->capacity = capacity; + bp->length = 0; + return _pb2hb(bp); +} + +_HPy_HIDDEN int +ctx_UnicodeBuilder_Add(HPyContext *ctx, HPyUnicodeBuilder builder, const char *item) +{ + _PyUnicodeBuilder_s *bp = _hb2pb(builder); + // TODO: Should we trust the user to submit a 0 terminated string? + // The alternative would be to use strnlen and have a maximum allowed length for s + int len = strlen(item); + if(bp->length + len >= bp->capacity) { + // TODO: Have a better reallocation strategy + int new_size = bp->capacity + len + 1; + bp->buf = realloc(bp->buf, new_size); + if(bp->buf == NULL) { + free(bp); + HPyErr_NoMemory(ctx); + return -1; + } + } + strncpy((bp->buf + bp->length), item, (bp->capacity - bp->length)); + bp->length += len; + return 0; +} + +_HPy_HIDDEN HPy +ctx_UnicodeBuilder_Build(HPyContext *ctx, HPyUnicodeBuilder builder) +{ + _PyUnicodeBuilder_s *bp = _hb2pb(builder); + HPy h_result = HPyUnicode_FromString(ctx, bp->buf); + free(bp->buf); + free(bp); + return h_result; +} + +_HPy_HIDDEN void +ctx_UnicodeBuilder_Cancel(HPyContext *ctx, HPyUnicodeBuilder builder) +{ + _PyUnicodeBuilder_s *bp = _hb2pb(builder); + free(bp->buf); + free(bp); +} diff --git a/hpy/tools/autogen/parse.py b/hpy/tools/autogen/parse.py index 149ddf82f..365147007 100644 --- a/hpy/tools/autogen/parse.py +++ b/hpy/tools/autogen/parse.py @@ -219,6 +219,10 @@ def _visit_hpyslot_slot(self, node): 'HPyTracker_Add': None, 'HPyTracker_ForgetAll': None, 'HPyTracker_Close': None, + 'HPyUnicodeBuilder_New': None, + 'HPyUnicodeBuilder_Add': None, + 'HPyUnicodeBuilder_Build': None, + 'HPyUnicodeBuilder_Cancel': None, '_HPy_Dump': None, 'HPy_Type': 'PyObject_Type', 'HPy_TypeCheck': None, diff --git a/hpy/tools/autogen/public_api.h b/hpy/tools/autogen/public_api.h index 34c0a1c67..d68bf0994 100644 --- a/hpy/tools/autogen/public_api.h +++ b/hpy/tools/autogen/public_api.h @@ -12,6 +12,7 @@ typedef int size_t; typedef int HPyFunc_Signature; typedef int cpy_PyObject; typedef int HPyListBuilder; +typedef int HPyUnicodeBuilder; typedef int HPyTupleBuilder; typedef int HPyTracker; typedef int HPy_RichCmpOp; @@ -282,6 +283,11 @@ void HPyListBuilder_Set(HPyContext *ctx, HPyListBuilder builder, HPy HPyListBuilder_Build(HPyContext *ctx, HPyListBuilder builder); void HPyListBuilder_Cancel(HPyContext *ctx, HPyListBuilder builder); +HPyUnicodeBuilder HPyUnicodeBuilder_New(HPyContext *ctx, HPy_ssize_t size); +int HPyUnicodeBuilder_Add(HPyContext *ctx, HPyUnicodeBuilder builder, const char *item); +HPy HPyUnicodeBuilder_Build(HPyContext *ctx, HPyUnicodeBuilder builder); +void HPyUnicodeBuilder_Cancel(HPyContext *ctx, HPyUnicodeBuilder builder); + HPyTupleBuilder HPyTupleBuilder_New(HPyContext *ctx, HPy_ssize_t initial_size); void HPyTupleBuilder_Set(HPyContext *ctx, HPyTupleBuilder builder, HPy_ssize_t index, HPy h_item); diff --git a/hpy/universal/src/autogen_ctx_def.h b/hpy/universal/src/autogen_ctx_def.h index 614be60c3..c537790ca 100644 --- a/hpy/universal/src/autogen_ctx_def.h +++ b/hpy/universal/src/autogen_ctx_def.h @@ -136,6 +136,10 @@ struct _HPyContext_s g_universal_ctx = { .ctx_ListBuilder_Set = &ctx_ListBuilder_Set, .ctx_ListBuilder_Build = &ctx_ListBuilder_Build, .ctx_ListBuilder_Cancel = &ctx_ListBuilder_Cancel, + .ctx_UnicodeBuilder_New = &ctx_UnicodeBuilder_New, + .ctx_UnicodeBuilder_Add = &ctx_UnicodeBuilder_Add, + .ctx_UnicodeBuilder_Build = &ctx_UnicodeBuilder_Build, + .ctx_UnicodeBuilder_Cancel = &ctx_UnicodeBuilder_Cancel, .ctx_TupleBuilder_New = &ctx_TupleBuilder_New, .ctx_TupleBuilder_Set = &ctx_TupleBuilder_Set, .ctx_TupleBuilder_Build = &ctx_TupleBuilder_Build, diff --git a/hpy/universal/src/ctx.c b/hpy/universal/src/ctx.c index 7cd371a47..f23d6eb7c 100644 --- a/hpy/universal/src/ctx.c +++ b/hpy/universal/src/ctx.c @@ -6,6 +6,7 @@ #include "common/runtime/ctx_module.h" #include "common/runtime/ctx_object.h" #include "common/runtime/ctx_listbuilder.h" +#include "common/runtime/ctx_unicodebuilder.h" #include "common/runtime/ctx_tracker.h" #include "common/runtime/ctx_tuple.h" #include "common/runtime/ctx_tuplebuilder.h" diff --git a/setup.py b/setup.py index 991cee993..b3b1fb0b2 100644 --- a/setup.py +++ b/setup.py @@ -61,6 +61,7 @@ def get_scm_config(): 'hpy/devel/src/runtime/ctx_listbuilder.c', 'hpy/devel/src/runtime/ctx_tuple.c', 'hpy/devel/src/runtime/ctx_tuplebuilder.c', + 'hpy/devel/src/runtime/ctx_unicodebuilder.c', 'hpy/debug/src/debug_ctx.c', 'hpy/debug/src/debug_ctx_cpython.c', 'hpy/debug/src/debug_handles.c', diff --git a/test/test_hpyunicodebuilder.py b/test/test_hpyunicodebuilder.py new file mode 100644 index 000000000..8bb1f9c4d --- /dev/null +++ b/test/test_hpyunicodebuilder.py @@ -0,0 +1,23 @@ +from .support import HPyTest + +class TestString(HPyTest): + def test_unicode_builder(self): + mod = self.make_module(""" + HPyDef_METH(f, "f", f_impl, HPyFunc_NOARGS) + static HPy f_impl(HPyContext *ctx, HPy h_self) + { + HPyUnicodeBuilder builder = HPyUnicodeBuilder_New(ctx, 0); + if(HPy_IsNull(builder)) { + HPyErr_SetString(ctx, ctx->h_RuntimeError, "Could not create HPyUnicodeBuilder"); + return HPy_NULL; + } + HPyUnicodeBuilder_Add(ctx, builder, "hello "); + HPyUnicodeBuilder_Add(ctx, builder, "world"); + HPyUnicodeBuilder_Add(ctx, builder, "!"); + HPy h_string = HPyUnicodeBuilder_Build(ctx, builder); + return h_string; + } + @EXPORT(f) + @INIT + """) + assert mod.f() == "hello world!"