codegen.ex (3488B)
1 defmodule Jason.Codegen do 2 @moduledoc false 3 4 alias Jason.{Encode, EncodeError} 5 6 def jump_table(ranges, default) do 7 ranges 8 |> ranges_to_orddict() 9 |> :array.from_orddict(default) 10 |> :array.to_orddict() 11 end 12 13 def jump_table(ranges, default, max) do 14 ranges 15 |> ranges_to_orddict() 16 |> :array.from_orddict(default) 17 |> resize(max) 18 |> :array.to_orddict() 19 end 20 21 defmacro bytecase(var, do: clauses) do 22 {ranges, default, literals} = clauses_to_ranges(clauses, []) 23 24 jump_table = jump_table(ranges, default) 25 26 quote do 27 case unquote(var) do 28 unquote(jump_table_to_clauses(jump_table, literals)) 29 end 30 end 31 end 32 33 defmacro bytecase(var, max, do: clauses) do 34 {ranges, default, empty} = clauses_to_ranges(clauses, []) 35 36 jump_table = jump_table(ranges, default, max) 37 38 quote do 39 case unquote(var) do 40 unquote(jump_table_to_clauses(jump_table, empty)) 41 end 42 end 43 end 44 45 def build_kv_iodata(kv, encode_args) do 46 elements = 47 kv 48 |> Enum.map(&encode_pair(&1, encode_args)) 49 |> Enum.intersperse(",") 50 51 collapse_static(List.flatten(["{", elements] ++ '}')) 52 end 53 54 defp clauses_to_ranges([{:->, _, [[{:in, _, [byte, range]}, rest], action]} | tail], acc) do 55 clauses_to_ranges(tail, [{range, {byte, rest, action}} | acc]) 56 end 57 58 defp clauses_to_ranges([{:->, _, [[default, rest], action]} | tail], acc) do 59 {Enum.reverse(acc), {default, rest, action}, literal_clauses(tail)} 60 end 61 62 defp literal_clauses(clauses) do 63 Enum.map(clauses, fn {:->, _, [[literal], action]} -> 64 {literal, action} 65 end) 66 end 67 68 defp jump_table_to_clauses([{val, {{:_, _, _}, rest, action}} | tail], empty) do 69 quote do 70 <<unquote(val), unquote(rest)::bits>> -> 71 unquote(action) 72 end ++ jump_table_to_clauses(tail, empty) 73 end 74 75 defp jump_table_to_clauses([{val, {byte, rest, action}} | tail], empty) do 76 quote do 77 <<unquote(byte), unquote(rest)::bits>> when unquote(byte) === unquote(val) -> 78 unquote(action) 79 end ++ jump_table_to_clauses(tail, empty) 80 end 81 82 defp jump_table_to_clauses([], literals) do 83 Enum.flat_map(literals, fn {pattern, action} -> 84 quote do 85 unquote(pattern) -> 86 unquote(action) 87 end 88 end) 89 end 90 91 defp resize(array, size), do: :array.resize(size, array) 92 93 defp ranges_to_orddict(ranges) do 94 ranges 95 |> Enum.flat_map(fn 96 {int, value} when is_integer(int) -> 97 [{int, value}] 98 99 {enum, value} -> 100 Enum.map(enum, &{&1, value}) 101 end) 102 |> :orddict.from_list() 103 end 104 105 defp encode_pair({key, value}, encode_args) do 106 key = IO.iodata_to_binary(Encode.key(key, &escape_key/3)) 107 key = "\"" <> key <> "\":" 108 [key, quote(do: Encode.value(unquote(value), unquote_splicing(encode_args)))] 109 end 110 111 defp escape_key(binary, _original, _skip) do 112 check_safe_key!(binary) 113 binary 114 end 115 116 defp check_safe_key!(binary) do 117 for <<(<<byte>> <- binary)>> do 118 if byte > 0x7F or byte < 0x1F or byte in '"\\/' do 119 raise EncodeError, 120 "invalid byte #{inspect(byte, base: :hex)} in literal key: #{inspect(binary)}" 121 end 122 end 123 124 :ok 125 end 126 127 defp collapse_static([bin1, bin2 | rest]) when is_binary(bin1) and is_binary(bin2) do 128 collapse_static([bin1 <> bin2 | rest]) 129 end 130 131 defp collapse_static([other | rest]) do 132 [other | collapse_static(rest)] 133 end 134 135 defp collapse_static([]) do 136 [] 137 end 138 end