Skip to content

Commit 4841201

Browse files
[3.14] gh-132983: Convert dict_content to take Py_buffer in ZstdDict() (GH-133924) (#134723)
gh-132983: Convert dict_content to take Py_buffer in ``ZstdDict()`` (GH-133924) (cherry picked from commit f2ce4bb) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
1 parent 117bb29 commit 4841201

File tree

5 files changed

+97
-73
lines changed

5 files changed

+97
-73
lines changed

Modules/_zstd/clinic/zstddict.c.h

Lines changed: 38 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_zstd/compressor.c

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,8 @@ _get_CDict(ZstdDict *self, int compressionLevel)
181181
}
182182
if (capsule == NULL) {
183183
/* Create ZSTD_CDict instance */
184-
char *dict_buffer = PyBytes_AS_STRING(self->dict_content);
185-
Py_ssize_t dict_len = Py_SIZE(self->dict_content);
186184
Py_BEGIN_ALLOW_THREADS
187-
cdict = ZSTD_createCDict(dict_buffer,
188-
dict_len,
185+
cdict = ZSTD_createCDict(self->dict_buffer, self->dict_len,
189186
compressionLevel);
190187
Py_END_ALLOW_THREADS
191188

@@ -244,17 +241,13 @@ _zstd_load_impl(ZstdCompressor *self, ZstdDict *zd,
244241
else if (type == DICT_TYPE_UNDIGESTED) {
245242
/* Load a dictionary.
246243
It doesn't override compression context's parameters. */
247-
zstd_ret = ZSTD_CCtx_loadDictionary(
248-
self->cctx,
249-
PyBytes_AS_STRING(zd->dict_content),
250-
Py_SIZE(zd->dict_content));
244+
zstd_ret = ZSTD_CCtx_loadDictionary(self->cctx, zd->dict_buffer,
245+
zd->dict_len);
251246
}
252247
else if (type == DICT_TYPE_PREFIX) {
253248
/* Load a prefix */
254-
zstd_ret = ZSTD_CCtx_refPrefix(
255-
self->cctx,
256-
PyBytes_AS_STRING(zd->dict_content),
257-
Py_SIZE(zd->dict_content));
249+
zstd_ret = ZSTD_CCtx_refPrefix(self->cctx, zd->dict_buffer,
250+
zd->dict_len);
258251
}
259252
else {
260253
Py_UNREACHABLE();

Modules/_zstd/decompressor.c

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,8 @@ _get_DDict(ZstdDict *self)
6868

6969
if (self->d_dict == NULL) {
7070
/* Create ZSTD_DDict instance from dictionary content */
71-
char *dict_buffer = PyBytes_AS_STRING(self->dict_content);
72-
Py_ssize_t dict_len = Py_SIZE(self->dict_content);
7371
Py_BEGIN_ALLOW_THREADS
74-
ret = ZSTD_createDDict(dict_buffer, dict_len);
72+
ret = ZSTD_createDDict(self->dict_buffer, self->dict_len);
7573
Py_END_ALLOW_THREADS
7674
self->d_dict = ret;
7775

@@ -156,17 +154,13 @@ _zstd_load_impl(ZstdDecompressor *self, ZstdDict *zd,
156154
}
157155
else if (type == DICT_TYPE_UNDIGESTED) {
158156
/* Load a dictionary */
159-
zstd_ret = ZSTD_DCtx_loadDictionary(
160-
self->dctx,
161-
PyBytes_AS_STRING(zd->dict_content),
162-
Py_SIZE(zd->dict_content));
157+
zstd_ret = ZSTD_DCtx_loadDictionary(self->dctx, zd->dict_buffer,
158+
zd->dict_len);
163159
}
164160
else if (type == DICT_TYPE_PREFIX) {
165161
/* Load a prefix */
166-
zstd_ret = ZSTD_DCtx_refPrefix(
167-
self->dctx,
168-
PyBytes_AS_STRING(zd->dict_content),
169-
Py_SIZE(zd->dict_content));
162+
zstd_ret = ZSTD_DCtx_refPrefix(self->dctx, zd->dict_buffer,
163+
zd->dict_len);
170164
}
171165
else {
172166
/* Impossible code path */

Modules/_zstd/zstddict.c

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class _zstd.ZstdDict "ZstdDict *" "&zstd_dict_type_spec"
2626
/*[clinic input]
2727
@classmethod
2828
_zstd.ZstdDict.__new__ as _zstd_ZstdDict_new
29-
dict_content: object
29+
dict_content: Py_buffer
3030
The content of a Zstandard dictionary as a bytes-like object.
3131
/
3232
*
@@ -42,17 +42,25 @@ by multiple ZstdCompressor or ZstdDecompressor objects.
4242
[clinic start generated code]*/
4343

4444
static PyObject *
45-
_zstd_ZstdDict_new_impl(PyTypeObject *type, PyObject *dict_content,
45+
_zstd_ZstdDict_new_impl(PyTypeObject *type, Py_buffer *dict_content,
4646
int is_raw)
47-
/*[clinic end generated code: output=3ebff839cb3be6d7 input=6b5de413869ae878]*/
47+
/*[clinic end generated code: output=685b7406a48b0949 input=9e8c493e31c98383]*/
4848
{
49+
/* All dictionaries must be at least 8 bytes */
50+
if (dict_content->len < 8) {
51+
PyErr_SetString(PyExc_ValueError,
52+
"Zstandard dictionary content too short "
53+
"(must have at least eight bytes)");
54+
return NULL;
55+
}
56+
4957
ZstdDict* self = PyObject_GC_New(ZstdDict, type);
5058
if (self == NULL) {
51-
goto error;
59+
return NULL;
5260
}
5361

54-
self->dict_content = NULL;
5562
self->d_dict = NULL;
63+
self->dict_buffer = NULL;
5664
self->dict_id = 0;
5765
self->lock = (PyMutex){0};
5866

@@ -62,39 +70,26 @@ _zstd_ZstdDict_new_impl(PyTypeObject *type, PyObject *dict_content,
6270
goto error;
6371
}
6472

65-
/* Check dict_content's type */
66-
self->dict_content = PyBytes_FromObject(dict_content);
67-
if (self->dict_content == NULL) {
68-
PyErr_SetString(PyExc_TypeError,
69-
"dict_content argument should be bytes-like object.");
70-
goto error;
71-
}
72-
73-
/* Both ordinary dictionary and "raw content" dictionary should
74-
at least 8 bytes */
75-
if (Py_SIZE(self->dict_content) < 8) {
76-
PyErr_SetString(PyExc_ValueError,
77-
"Zstandard dictionary content should at least "
78-
"8 bytes.");
73+
self->dict_buffer = PyMem_Malloc(dict_content->len);
74+
if (!self->dict_buffer) {
75+
PyErr_NoMemory();
7976
goto error;
8077
}
78+
memcpy(self->dict_buffer, dict_content->buf, dict_content->len);
79+
self->dict_len = dict_content->len;
8180

8281
/* Get dict_id, 0 means "raw content" dictionary. */
83-
self->dict_id = ZSTD_getDictID_fromDict(
84-
PyBytes_AS_STRING(self->dict_content),
85-
Py_SIZE(self->dict_content));
82+
self->dict_id = ZSTD_getDictID_fromDict(self->dict_buffer, self->dict_len);
8683

8784
/* Check validity for ordinary dictionary */
8885
if (!is_raw && self->dict_id == 0) {
89-
char *msg = "Invalid Zstandard dictionary and is_raw not set.\n";
90-
PyErr_SetString(PyExc_ValueError, msg);
86+
PyErr_SetString(PyExc_ValueError, "invalid Zstandard dictionary");
9187
goto error;
9288
}
9389

94-
// Can only track self once self->dict_content is included
9590
PyObject_GC_Track(self);
9691

97-
return (PyObject*)self;
92+
return (PyObject *)self;
9893

9994
error:
10095
Py_XDECREF(self);
@@ -115,12 +110,12 @@ ZstdDict_dealloc(PyObject *ob)
115110

116111
assert(!PyMutex_IsLocked(&self->lock));
117112

118-
/* Release dict_content after Free ZSTD_CDict/ZSTD_DDict instances */
119-
Py_CLEAR(self->dict_content);
113+
/* Release dict_buffer after freeing ZSTD_CDict/ZSTD_DDict instances */
114+
PyMem_Free(self->dict_buffer);
120115
Py_CLEAR(self->c_dicts);
121116

122117
PyTypeObject *tp = Py_TYPE(self);
123-
PyObject_GC_Del(ob);
118+
tp->tp_free(self);
124119
Py_DECREF(tp);
125120
}
126121

@@ -131,25 +126,33 @@ PyDoc_STRVAR(ZstdDict_dictid_doc,
131126
"The special value '0' means a 'raw content' dictionary,"
132127
"without any restrictions on format or content.");
133128

134-
PyDoc_STRVAR(ZstdDict_dictcontent_doc,
135-
"The content of a Zstandard dictionary, as a bytes object.");
136-
137129
static PyObject *
138-
ZstdDict_str(PyObject *ob)
130+
ZstdDict_repr(PyObject *ob)
139131
{
140132
ZstdDict *dict = ZstdDict_CAST(ob);
141133
return PyUnicode_FromFormat("<ZstdDict dict_id=%u dict_size=%zd>",
142-
dict->dict_id, Py_SIZE(dict->dict_content));
134+
(unsigned int)dict->dict_id, dict->dict_len);
143135
}
144136

145137
static PyMemberDef ZstdDict_members[] = {
146-
{"dict_id", Py_T_UINT, offsetof(ZstdDict, dict_id), Py_READONLY,
147-
ZstdDict_dictid_doc},
148-
{"dict_content", Py_T_OBJECT_EX, offsetof(ZstdDict, dict_content),
149-
Py_READONLY, ZstdDict_dictcontent_doc},
138+
{"dict_id", Py_T_UINT, offsetof(ZstdDict, dict_id), Py_READONLY, ZstdDict_dictid_doc},
150139
{NULL}
151140
};
152141

142+
/*[clinic input]
143+
@getter
144+
_zstd.ZstdDict.dict_content
145+
146+
The content of a Zstandard dictionary, as a bytes object.
147+
[clinic start generated code]*/
148+
149+
static PyObject *
150+
_zstd_ZstdDict_dict_content_get_impl(ZstdDict *self)
151+
/*[clinic end generated code: output=0d05caa5b550eabb input=4ed526d1c151c596]*/
152+
{
153+
return PyBytes_FromStringAndSize(self->dict_buffer, self->dict_len);
154+
}
155+
153156
/*[clinic input]
154157
@getter
155158
_zstd.ZstdDict.as_digested_dict
@@ -219,6 +222,7 @@ _zstd_ZstdDict_as_prefix_get_impl(ZstdDict *self)
219222
}
220223

221224
static PyGetSetDef ZstdDict_getset[] = {
225+
_ZSTD_ZSTDDICT_DICT_CONTENT_GETSETDEF
222226
_ZSTD_ZSTDDICT_AS_DIGESTED_DICT_GETSETDEF
223227
_ZSTD_ZSTDDICT_AS_UNDIGESTED_DICT_GETSETDEF
224228
_ZSTD_ZSTDDICT_AS_PREFIX_GETSETDEF
@@ -229,24 +233,22 @@ static Py_ssize_t
229233
ZstdDict_length(PyObject *ob)
230234
{
231235
ZstdDict *self = ZstdDict_CAST(ob);
232-
assert(PyBytes_Check(self->dict_content));
233-
return Py_SIZE(self->dict_content);
236+
return self->dict_len;
234237
}
235238

236239
static int
237240
ZstdDict_traverse(PyObject *ob, visitproc visit, void *arg)
238241
{
239242
ZstdDict *self = ZstdDict_CAST(ob);
240243
Py_VISIT(self->c_dicts);
241-
Py_VISIT(self->dict_content);
242244
return 0;
243245
}
244246

245247
static int
246248
ZstdDict_clear(PyObject *ob)
247249
{
248250
ZstdDict *self = ZstdDict_CAST(ob);
249-
Py_CLEAR(self->dict_content);
251+
Py_CLEAR(self->c_dicts);
250252
return 0;
251253
}
252254

@@ -255,7 +257,7 @@ static PyType_Slot zstddict_slots[] = {
255257
{Py_tp_getset, ZstdDict_getset},
256258
{Py_tp_new, _zstd_ZstdDict_new},
257259
{Py_tp_dealloc, ZstdDict_dealloc},
258-
{Py_tp_str, ZstdDict_str},
260+
{Py_tp_repr, ZstdDict_repr},
259261
{Py_tp_doc, (void *)_zstd_ZstdDict_new__doc__},
260262
{Py_sq_length, ZstdDict_length},
261263
{Py_tp_traverse, ZstdDict_traverse},

Modules/_zstd/zstddict.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ typedef struct {
1515
ZSTD_DDict *d_dict;
1616
PyObject *c_dicts;
1717

18-
/* Content of the dictionary, bytes object. */
19-
PyObject *dict_content;
18+
/* Dictionary content. */
19+
char *dict_buffer;
20+
Py_ssize_t dict_len;
21+
2022
/* Dictionary id */
2123
uint32_t dict_id;
2224

0 commit comments

Comments
 (0)