package stramon-lib

  1. Overview
  2. Docs

Source file string_utils.ml

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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
let count_backslash_backward s i =
  if i < 0 || i >= String.length s then (
    invalid_arg "count_backslash_backward: invalid i"
  );
  let rec aux i e =
    if i < 0 || s.[i] <> '\\' then
      e - i
    else
      aux (i - 1) e
  in
  aux i i

let has_trailing_escape s =
  let len = String.length s in
  count_backslash_backward s (len - 1) mod 2 = 1

let concat_file_names names =
  let splits =
    names
    |> List.map (fun s ->
        String.split_on_char '/' s)
    |> List.concat
    |> List.filter (fun s -> s <> "")
  in
  let res = String.concat Filename.dir_sep splits in
  match names with
  | [] -> res
  | x :: _ -> if String.sub x 0 1 = "/" then "/" ^ res else res

let int_of_hex_digit c =
  match c with
  | '0' .. '9' -> Some (Char.code c - Char.code '0')
  | 'A' .. 'F' -> Some (0xA + (Char.code c - Char.code 'A'))
  | 'a' .. 'f' -> Some (0xA + (Char.code c - Char.code 'a'))
  | _ -> None

let string_of_hex_string ?(preamble_before_each_byte = "") (s : string) : string option =
  let s_len = String.length s in
  let preamble_size = String.length preamble_before_each_byte in
  let rec preamble_is_okay s offset pos =
    pos >= preamble_size
    || (s.[offset] = preamble_before_each_byte.[pos]
        && preamble_is_okay s (offset + 1) (pos + 1))
  in
  let chunk_size = preamble_size + 2 in
  if s_len mod chunk_size <> 0 then
    None
  else
    let buf_len = s_len / chunk_size in
    let buf = Bytes.make buf_len '\x00' in
    let rec aux i =
      if i >= buf_len then
        Some (Bytes.to_string buf)
      else (
        let j = i * chunk_size in
        if preamble_is_okay s j 0 then (
          match int_of_hex_digit s.[j+preamble_size+0], int_of_hex_digit s.[j+preamble_size+1] with
          | Some a, Some b ->
            Bytes.set buf i (Char.chr (a * 0x10 + b));
            aux (succ i)
          | _, _ ->
            None
        )
        else
          None
      )
    in
    aux 0

let octal_of_string (s : string) : int64 option =
  let len = String.length s in
  let max_pos = len - 1 in
  let max_digit_count = 64 / 3 in
  let rec aux acc pos =
    if pos < 0 then
      Some acc
    else (
      match s.[pos] with
      | '0' .. '7' as c -> (
          let x = Int64.of_int (Char.code c - Char.code '0') in
          let y = Int64.shift_left x (3 * (max_pos - pos)) in
          aux (Int64.logor acc y) (pos - 1)
        )
      | _ -> None
    )
  in
  if len > max_digit_count || len = 0 then (
    None
  ) else (
    aux 0L (len - 1)
  )

let hex_of_string (s : string) : int64 option =
  let len = String.length s in
  let max_pos = len - 1 in
  let max_digit_count = 64 / 4 in
  let rec aux acc pos =
    if pos < 0 then
      Some acc
    else (
      match int_of_hex_digit s.[pos] with
      | Some x -> (
          let x = Int64.of_int x in
          let y = Int64.shift_left x (4 * (max_pos - pos)) in
          aux (Int64.logor acc y) (pos - 1)
        )
      | _ -> None
    )
  in
  if len > max_digit_count || len = 0 then (
    None
  ) else (
    aux 0L (len - 1)
  )

let find_char ?(start = 0) (c : char) (s : string) : int option =
  let str_len = String.length s in
  let rec aux i =
    if i >= str_len then None
    else
    if c = s.[i] then Some i
    else
      aux (succ i)
  in
  aux start

let find_char_rev ?(start : int option) (c : char) (s : string) : int option =
  let str_len = String.length s in
  let start = Option.value ~default:(str_len - 1) start  in
  let rec aux i =
    if i <= 0 then None
    else
    if c = s.[i] then Some i
    else
      aux (pred i)
  in
  aux start

let remove_c_comments (s : string) : string =
  let str_len = String.length s in
  let rec shift_until_closing s i =
    if i >= str_len then
      i
    else (
      if i <= str_len-1 && s.[i] = '*' && s.[i+1] = '/' then
        i+2
      else
        shift_until_closing s (i+1)
    )
  in
  let rec aux acc s last_seg_end i =
    let maybe_add_to_acc acc s last_seg_end i =
      if last_seg_end < i then
        StringLabels.sub ~pos:last_seg_end ~len:(i - last_seg_end) s :: acc
      else
        acc
    in
    if i >= str_len then
      let acc = maybe_add_to_acc acc s last_seg_end i in
      String.concat "" (List.rev acc)
    else (
      if s.[i] = '/' && s.[i+1] = '*' then
        let skip_to = shift_until_closing s (i+2) in
        let acc = maybe_add_to_acc acc s last_seg_end i in
        aux acc s skip_to skip_to
      else
        aux acc s last_seg_end (i+1)
    )
  in
  aux [] s 0 0