Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# Pretty printer for smol_str::SmolStr
#
# Usage (any of these):
#   (gdb) source /path/to/gdb_smolstr_printer.py
# or add to .gdbinit
#   python
#   import gdb
#   gdb.execute("source /path/to/gdb_smolstr_printer.py")
#   end
#
# After loading:
#   (gdb) info pretty-printer
#     ...
#     global pretty-printers:
#       smol_str
#         SmolStr
#
# Disable/enable:
#   (gdb) disable pretty-printer global smol_str SmolStr
#   (gdb) enable  pretty-printer global smol_str SmolStr

import gdb
import gdb.printing
import re

SMOL_INLINE_SIZE_RE = re.compile(r".*::_V(\d+)$")


def _read_utf8(mem):
    try:
        return mem.tobytes().decode("utf-8", errors="replace")
    except Exception:
        return repr(mem.tobytes())


def _active_variant(enum_val):
    """Return (variant_name, variant_value) for a Rust enum value using discriminant logic.
    Assume layout: fields[0] is unnamed u8 discriminant; fields[1] is the active variant.
    """
    fields = enum_val.type.fields()
    if len(fields) < 2:
        return None, None
    variant_field = fields[1]
    return variant_field.name, enum_val[variant_field]


class SmolStrProvider:
    def __init__(self, val):
        self.val = val

    def to_string(self):
        try:
            repr_enum = self.val["__0"]
        except Exception:
            return "<SmolStr: missing __0>"

        variant_name, variant_val = _active_variant(repr_enum)
        if not variant_name:
            return "<SmolStr: unknown variant>"

        if variant_name == "Inline":
            try:
                inline_len_val = variant_val["len"]
                m = SMOL_INLINE_SIZE_RE.match(str(inline_len_val))
                if not m:
                    return "<SmolStr Inline: bad len>"
                length = int(m.group(1))
                buf = variant_val["buf"]
                data = bytes(int(buf[i]) for i in range(length))
                return data.decode("utf-8", errors="replace")
            except Exception as e:
                return f"<SmolStr Inline error: {e}>"

        if variant_name == "Static":
            try:
                # variant_val["__0"] is &'static str
                return variant_val["__0"]
            except Exception as e:
                return f"<SmolStr Static error: {e}>"

        if variant_name == "Heap":
            try:
                # variant_val["__0"] is an Arc<str>
                inner = variant_val["__0"]["ptr"]["pointer"]
                # inner is a fat pointer to ArcInner<str>
                data_ptr = inner["data_ptr"]
                length = int(inner["length"])
                # ArcInner layout:
                # strong: Atomic<usize>, weak: Atomic<usize> | unsized tail 'data' bytes.
                sizeof_AtomicUsize = gdb.lookup_type(
                    "core::sync::atomic::AtomicUsize"
                ).sizeof
                header_size = sizeof_AtomicUsize * 2  # strong + weak counters
                data_arr = int(data_ptr) + header_size
                mem = gdb.selected_inferior().read_memory(data_arr, length)
                return _read_utf8(mem)
            except Exception as e:
                return f"<SmolStr Heap error: {e}>"

        return f"<SmolStr: unhandled variant {variant_name}>"

    def display_hint(self):
        return "string"


class SmolStrSubPrinter(gdb.printing.SubPrettyPrinter):
    def __init__(self):
        super(SmolStrSubPrinter, self).__init__("SmolStr")

    def __call__(self, val):
        if not self.enabled:
            return None
        try:
            t = val.type.strip_typedefs()
            if t.code == gdb.TYPE_CODE_STRUCT and t.name == "smol_str::SmolStr":
                return SmolStrProvider(val)
        except Exception:
            pass
        return None


class SmolStrPrettyPrinter(gdb.printing.PrettyPrinter):
    def __init__(self):
        super(SmolStrPrettyPrinter, self).__init__("smol_str", [])
        self.subprinters = []
        self._sp = SmolStrSubPrinter()
        self.subprinters.append(self._sp)

    def __call__(self, val):
        # Iterate subprinters (only one now, scalable for future)
        for sp in self.subprinters:
            pp = sp(val)
            if pp is not None:
                return pp
        return None


printer = SmolStrPrettyPrinter()


def register_printers(objfile=None):
    gdb.printing.register_pretty_printer(objfile, printer, replace=True)


register_printers()