@@ -1088,14 +1088,13 @@ defmodule String do
1088
1088
iex> String.slice("a", 1..1500)
1089
1089
""
1090
1090
1091
- iex> String.slice("a", 2..1500)
1092
- ""
1093
-
1094
1091
"""
1095
1092
@ spec slice ( t , Range . t ) :: t
1096
1093
1097
1094
def slice ( string , range )
1098
1095
1096
+ def slice ( "" , _ .. _ ) , do: ""
1097
+
1099
1098
def slice ( string , first .. - 1 ) when first >= 0 do
1100
1099
nbytes = count_bytes_until ( string , first )
1101
1100
if nbytes >= 0 do
@@ -1109,21 +1108,22 @@ defmodule String do
1109
1108
do_slice ( next_grapheme ( string ) , string , first , last , 0 , 0 , 0 )
1110
1109
end
1111
1110
1112
- def slice ( string , first .. last ) do
1113
- total = length ( string )
1114
-
1115
- if first < 0 do
1116
- first = total + first
1117
- end
1111
+ def slice ( string , first .. last ) when first >= 0 do
1112
+ count = abs ( last )
1113
+ do_slice_neg_lb ( next_grapheme ( string ) , string , first , 0 , count , init_bytes )
1114
+ end
1118
1115
1119
- if last < 0 do
1120
- last = total + last
1121
- end
1116
+ def slice ( string , first .. last ) when last >= 0 do
1117
+ count = abs ( first )
1118
+ do_slice_neg ( next_grapheme ( string ) , string , first , last , count , 0 , init_bytes )
1119
+ end
1122
1120
1123
- if first >= 0 do
1124
- do_slice ( next_grapheme ( string ) , string , first , last , 0 , 0 , 0 )
1125
- else
1121
+ def slice ( string , first .. last ) when first < 0 and last < 0 do
1122
+ if first > last do
1126
1123
""
1124
+ else
1125
+ count = abs ( first )
1126
+ do_slice_neg ( next_grapheme ( string ) , string , first , last , count , 0 , init_bytes )
1127
1127
end
1128
1128
end
1129
1129
@@ -1143,6 +1143,73 @@ defmodule String do
1143
1143
nbytes
1144
1144
end
1145
1145
1146
+ # "lb" stands for known lower bound
1147
+ defp do_slice_neg_lb ( nil , str , first , pos , count , bytes ) do
1148
+ cond do
1149
+ pos < first + 1 ->
1150
+ # starting position is out of bounds
1151
+ ""
1152
+ pos - count < first ->
1153
+ # the negative right bound is out of bounds
1154
+ ""
1155
+ true ->
1156
+ { bytes , _ , start_bytes } = bytes
1157
+ len_bytes = sum_bytes ( bytes , count - 1 )
1158
+ binary_part ( str , start_bytes , byte_size ( str ) - start_bytes - len_bytes )
1159
+ end
1160
+ end
1161
+
1162
+ defp do_slice_neg_lb ( { char , rest } , str , first , pos , count , bytes ) do
1163
+ bytes = update_bytes ( bytes , char , first , pos , count )
1164
+ do_slice_neg_lb ( next_grapheme ( rest ) , str , first , pos + 1 , count , bytes )
1165
+ end
1166
+
1167
+ # both bounds are negative
1168
+ defp do_slice_neg ( nil , str , first , last , count , pos , bytes ) do
1169
+ # get positive bounds
1170
+ if first < 0 , do: first = pos + first
1171
+ if last < 0 , do: last = pos + last
1172
+ cond do
1173
+ first < 0 or first > last ->
1174
+ # negative left bound is out of bounds
1175
+ ""
1176
+ true ->
1177
+ { bytes , _ , _ } = bytes
1178
+ str_bytes = byte_size ( str )
1179
+ start_bytes = str_bytes - sum_bytes ( bytes , count )
1180
+ last = min ( last , pos - 1 )
1181
+ len_bytes =
1182
+ bytes |> drop_bytes ( pos - last - 1 ) |> sum_bytes ( last - first + 1 )
1183
+ binary_part ( str , start_bytes , min ( len_bytes , str_bytes - start_bytes ) )
1184
+ end
1185
+ end
1186
+
1187
+ defp do_slice_neg ( { char , rest } , str , first , last , count , pos , bytes ) do
1188
+ bytes = update_bytes ( bytes , char , 0 , 0 , count )
1189
+ do_slice_neg ( next_grapheme ( rest ) , str , first , last , count , pos + 1 , bytes )
1190
+ end
1191
+
1192
+ defp init_bytes ( ) , do: { [ ] , 0 , 0 }
1193
+
1194
+ defp sum_bytes ( bytes , count ) do
1195
+ bytes |> Enum . take ( count ) |> Enum . sum
1196
+ end
1197
+
1198
+ defp drop_bytes ( bytes , count ) do
1199
+ Enum . drop ( bytes , count )
1200
+ end
1201
+
1202
+ defp update_bytes ( { bytes , n , start_bytes } , char , first , pos , cnt ) do
1203
+ char_bytes = byte_size ( char )
1204
+ if pos < first do
1205
+ start_bytes = start_bytes + char_bytes
1206
+ end
1207
+ if n < cnt do
1208
+ n = n + 1
1209
+ end
1210
+ { [ char_bytes | bytes ] , n , start_bytes }
1211
+ end
1212
+
1146
1213
defp do_slice ( _ , _ , start_pos , last_pos , _ , _ , _ ) when start_pos > last_pos do
1147
1214
""
1148
1215
end
0 commit comments