Skip to content

Commit 92afde2

Browse files
committed
class is done
1 parent 92d3598 commit 92afde2

File tree

2 files changed

+105
-4
lines changed

2 files changed

+105
-4
lines changed

CLAUDE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ This repository is being used as a learning environment for CPython internals. T
1515
- Reference `teaching-notes.md` for detailed research (student should not read this)
1616
- Encourage use of `dis` module, GDB, and debug builds for exploration
1717

18+
**IMPORTANT - Don't write code for the student:**
19+
- Give hints, not implementations
20+
- Point to similar code in tupleobject.c as reference
21+
- Explain what needs to happen, let them write it
22+
- If they're stuck on API: name the function/macro, don't write the call
23+
- Only write code if explicitly asked ("write this for me")
24+
- 2-3 line snippets for syntax are OK; 10+ line functions are NOT
25+
1826
**The learning project:** Implementing a `Record` type and `BUILD_RECORD` opcode (~300 LoC). This comprehensive project covers:
1927
- PyObject/PyVarObject fundamentals (custom struct, refcounting)
2028
- Type slots (tp_repr, tp_hash, tp_dealloc, tp_getattro, sq_length, sq_item)

Objects/recordobject.c

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,50 @@ static PySequenceMethods record_as_sequence = {
171171
0, /* sq_contains */
172172
};
173173

174+
#if SIZEOF_PY_UHASH_T > 4
175+
#define _PyHASH_XXPRIME_1 ((Py_uhash_t)11400714785074694791ULL)
176+
#define _PyHASH_XXPRIME_2 ((Py_uhash_t)14029467366897019727ULL)
177+
#define _PyHASH_XXPRIME_5 ((Py_uhash_t)2870177450012600261ULL)
178+
#define _PyHASH_XXROTATE(x) ((x << 31) | (x >> 33)) /* Rotate left 31 bits */
179+
#else
180+
#define _PyHASH_XXPRIME_1 ((Py_uhash_t)2654435761UL)
181+
#define _PyHASH_XXPRIME_2 ((Py_uhash_t)2246822519UL)
182+
#define _PyHASH_XXPRIME_5 ((Py_uhash_t)374761393UL)
183+
#define _PyHASH_XXROTATE(x) ((x << 13) | (x >> 19)) /* Rotate left 13 bits */
184+
#endif
185+
174186
static Py_hash_t
175187
record_hash(PyRecordObject *v)
176188
{
177-
// TODO
178-
return -1;
189+
Py_ssize_t i, len = Py_SIZE(v);
190+
PyObject **item = v->ob_item;
191+
PyObject *names = v->names;
192+
193+
Py_uhash_t acc = _PyHASH_XXPRIME_5;
194+
for (i = 0; i < len; i++) {
195+
Py_uhash_t lane = PyObject_Hash(item[i]);
196+
if (lane == (Py_uhash_t)-1) {
197+
return -1;
198+
}
199+
acc += lane * _PyHASH_XXPRIME_2;
200+
acc = _PyHASH_XXROTATE(acc);
201+
acc *= _PyHASH_XXPRIME_1;
202+
}
203+
Py_uhash_t lane = PyObject_Hash(names);
204+
if (lane == (Py_uhash_t)-1) {
205+
return -1;
206+
}
207+
acc += lane * _PyHASH_XXPRIME_2;
208+
acc = _PyHASH_XXROTATE(acc);
209+
acc *= _PyHASH_XXPRIME_1;
210+
211+
/* Add input length, mangled to keep the historical value of hash(()). */
212+
acc += len ^ (_PyHASH_XXPRIME_5 ^ 3527539UL);
213+
214+
if (acc == (Py_uhash_t)-1) {
215+
return 1546275796;
216+
}
217+
return acc;
179218
}
180219

181220
PyObject *
@@ -212,8 +251,62 @@ record_getattro(PyObject *obj, PyObject *name)
212251
static PyObject *
213252
record_rich_compare(PyObject *v, PyObject *w, int op)
214253
{
215-
// TODO
216-
return NULL;
254+
PyRecordObject *vr, *wr;
255+
Py_ssize_t i;
256+
Py_ssize_t vlen, wlen;
257+
258+
if (!PyRecord_Check(v) || !PyRecord_Check(w))
259+
Py_RETURN_NOTIMPLEMENTED;
260+
261+
vr = (PyRecordObject *)v;
262+
wr = (PyRecordObject *)w;
263+
264+
vlen = Py_SIZE(vr);
265+
wlen = Py_SIZE(wr);
266+
267+
/* Note: the corresponding code for lists has an "early out" test
268+
* here when op is EQ or NE and the lengths differ. That pays there,
269+
* but Tim was unable to find any real code where EQ/NE tuple
270+
* compares don't have the same length, so testing for it here would
271+
* have cost without benefit.
272+
*/
273+
274+
/* Search for the first index where items are different.
275+
* Note that because tuples are immutable, it's safe to reuse
276+
* vlen and wlen across the comparison calls.
277+
*/
278+
int k = PyObject_RichCompareBool(vr->names, wr->names, Py_EQ);
279+
if (k < 0)
280+
return NULL;
281+
if (!k) {
282+
if (op == Py_EQ) Py_RETURN_FALSE;
283+
if (op == Py_NE) Py_RETURN_TRUE;
284+
Py_RETURN_NOTIMPLEMENTED;
285+
}
286+
for (i = 0; i < vlen && i < wlen; i++) {
287+
int k = PyObject_RichCompareBool(vr->ob_item[i],
288+
wr->ob_item[i], Py_EQ);
289+
if (k < 0)
290+
return NULL;
291+
if (!k)
292+
break;
293+
}
294+
295+
if (i >= vlen || i >= wlen) {
296+
/* No more items to compare -- compare sizes */
297+
Py_RETURN_RICHCOMPARE(vlen, wlen, op);
298+
}
299+
300+
/* We have an item that differs -- shortcuts for EQ/NE */
301+
if (op == Py_EQ) {
302+
Py_RETURN_FALSE;
303+
}
304+
if (op == Py_NE) {
305+
Py_RETURN_TRUE;
306+
}
307+
308+
/* Compare the final item again using the proper operator */
309+
Py_RETURN_NOTIMPLEMENTED;
217310
}
218311

219312
static PyObject *

0 commit comments

Comments
 (0)