@@ -65,6 +65,12 @@ module Crystal::System
65
65
finish = ptr + format_len
66
66
arg_index = 0
67
67
68
+ # The widest integer types supported by the format specifier are `%lld` and
69
+ # `%llu`, which do not exceed 64 bits, so we only need 20 digits maximum
70
+ # note that `chars` does not have to be null-terminated, since we are
71
+ # only yielding a `Bytes`
72
+ int_chars = uninitialized UInt8 [20 ]
73
+
68
74
while ptr < finish
69
75
next_percent = ptr
70
76
while next_percent < finish && ! (next_percent.value === '%' )
@@ -94,20 +100,20 @@ module Crystal::System
94
100
end
95
101
when 'd'
96
102
read_arg(Int ::Primitive ) do |arg |
97
- to_int_slice(arg, 10 , true , width) { | bytes | yield bytes }
103
+ yield to_int_slice(int_chars.to_slice, arg, 10 , true , width)
98
104
end
99
105
when 'u'
100
106
read_arg(Int ::Primitive ) do |arg |
101
- to_int_slice(arg, 10 , false , width) { | bytes | yield bytes }
107
+ yield to_int_slice(int_chars.to_slice, arg, 10 , false , width)
102
108
end
103
109
when 'x'
104
110
read_arg(Int ::Primitive ) do |arg |
105
- to_int_slice(arg, 16 , false , width) { | bytes | yield bytes }
111
+ yield to_int_slice(int_chars.to_slice, arg, 16 , false , width)
106
112
end
107
113
when 'p'
108
114
read_arg(Pointer (Void )) do |arg |
109
115
yield " 0x" .to_slice
110
- to_int_slice(arg.address, 16 , false , 2 ) { | bytes | yield bytes }
116
+ yield to_int_slice(int_chars.to_slice, arg.address, 16 , false , 2 )
111
117
end
112
118
else
113
119
yield Slice .new(next_percent, fmt_ptr + 1 - next_percent)
@@ -118,8 +124,8 @@ module Crystal::System
118
124
end
119
125
120
126
private macro read_arg (type , & block )
121
- {{ block.args[0 ] }} = args[arg_index].as?({{ type }})
122
- if ! {{ block.args[0 ] }}.nil?
127
+ {{ block.args[0 ] }} = args[arg_index]
128
+ if {{ block.args[0 ] }}.is_a?({{ type }})
123
129
{{ block.body }}
124
130
else
125
131
yield " (???)" .to_slice
@@ -140,28 +146,27 @@ module Crystal::System
140
146
end
141
147
142
148
# simplified version of `Int#internal_to_s`
143
- protected def self.to_int_slice (num , base , signed , width , & )
149
+ protected def self.to_int_slice (buf , num , base , signed , width )
144
150
if num == 0
145
- yield " 0" .to_slice
146
- return
151
+ " 0" .to_slice
152
+ else
153
+ # NOTE: do not factor out `num`! it is written this way to inhibit
154
+ # unnecessary union dispatches
155
+ case {signed, width}
156
+ when {true , 2 } then to_int_slice_impl(buf, LibC ::LongLong .new!(num), base)
157
+ when {true , 1 } then to_int_slice_impl(buf, LibC ::Long .new!(num), base)
158
+ when {true , 0 } then to_int_slice_impl(buf, LibC ::Int .new!(num), base)
159
+ when {false , 2 } then to_int_slice_impl(buf, LibC ::ULongLong .new!(num), base)
160
+ when {false , 1 } then to_int_slice_impl(buf, LibC ::ULong .new!(num), base)
161
+ else to_int_slice_impl(buf, LibC ::UInt .new!(num), base)
162
+ end
147
163
end
164
+ end
148
165
149
- # Given sizeof(num) <= 64 bits, we need at most 20 bytes for `%d` or `%u`
150
- # note that `chars` does not have to be null-terminated, since we are
151
- # only yielding a `Bytes`
152
- chars = uninitialized UInt8 [20 ]
153
- ptr_end = chars.to_unsafe + 20
166
+ private def self.to_int_slice_impl (buf , num , base )
167
+ ptr_end = buf.to_unsafe + buf.size
154
168
ptr = ptr_end
155
169
156
- num = case {signed, width}
157
- when {true , 2 } then LibC ::LongLong .new!(num)
158
- when {true , 1 } then LibC ::Long .new!(num)
159
- when {true , 0 } then LibC ::Int .new!(num)
160
- when {false , 2 } then LibC ::ULongLong .new!(num)
161
- when {false , 1 } then LibC ::ULong .new!(num)
162
- else LibC ::UInt .new!(num)
163
- end
164
-
165
170
neg = num < 0
166
171
167
172
# do not assume Crystal constant initialization succeeds, hence not `DIGITS`
@@ -178,7 +183,7 @@ module Crystal::System
178
183
ptr.value = '-' .ord.to_u8
179
184
end
180
185
181
- yield Slice .new(ptr, ptr_end - ptr)
186
+ Slice .new(ptr, ptr_end - ptr)
182
187
end
183
188
184
189
def self.print_exception (message, ex)
0 commit comments