hyperscan-async 0.0.1

Async wrapper for the hyperscan C++ regex library.
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
/* Copyright 2022-2023 Danny McClanahan */
/* SPDX-License-Identifier: BSD-3-Clause */

//! ???

use crate::{hs, matchers::ExpressionIndex};

use displaydoc::Display;
use thiserror::Error;

use std::{
  ffi::{CStr, NulError},
  os::raw::c_uint,
};

#[derive(
  Debug,
  Display,
  Error,
  Copy,
  Clone,
  PartialEq,
  Eq,
  PartialOrd,
  Ord,
  Hash,
  num_enum::IntoPrimitive,
  num_enum::FromPrimitive,
)]
#[repr(i8)]
#[ignore_extra_doc_attributes]
pub enum HyperscanRuntimeError {
  /// A parameter passed to this function was invalid.
  ///
  /// This error is only returned in cases where the function can detect an
  /// invalid parameter -- it cannot be relied upon to detect (for example)
  /// pointers to freed memory or other invalid data.
  Invalid = hs::HS_INVALID,
  /// A memory allocation failed.
  NoMem = hs::HS_NOMEM,
  /// The engine was terminated by callback.
  ///
  /// This return value indicates that the target buffer was partially scanned,
  /// but that the callback function requested that scanning cease after a match
  /// was located.
  ScanTerminated = hs::HS_SCAN_TERMINATED,
  /// The pattern compiler failed, and the [`CompileError`] should be
  /// inspected for more detail.
  CompilerError = hs::HS_COMPILER_ERROR,
  /// The given database was built for a different version of Hyperscan.
  DbVersionError = hs::HS_DB_VERSION_ERROR,
  /// The given database was built for a different platform (i.e., CPU type).
  DbPlatformError = hs::HS_DB_PLATFORM_ERROR,
  /// The given database was built for a different mode of operation.
  ///
  /// This error is returned when streaming calls are used with a block or
  /// vectored database and vice versa.
  DbModeError = hs::HS_DB_MODE_ERROR,
  /// A parameter passed to this function was not correctly aligned.
  BadAlign = hs::HS_BAD_ALIGN,
  /// The memory allocator returned incorrectly aligned memory.
  ///
  /// The memory allocator (either `malloc()` or the allocator set with @ref
  /// hs_set_allocator()) did not correctly return memory suitably aligned for
  /// the largest representable data type on this platform.
  BadAlloc = hs::HS_BAD_ALLOC,
  /// The scratch region was already in use.
  ///
  /// This error is returned when Hyperscan is able to detect that the scratch
  /// region given is already in use by another Hyperscan API call.
  ///
  /// A separate scratch region, allocated with @ref hs_alloc_scratch() or @ref
  /// hs_clone_scratch(), is required for every concurrent caller of the
  /// Hyperscan API.
  ///
  /// For example, this error might be returned when @ref hs_scan() has been
  /// called inside a callback delivered by a currently-executing @ref hs_scan()
  /// call using the same scratch region.
  ///
  /// Note: Not all concurrent uses of scratch regions may be detected. This
  /// error is intended as a best-effort debugging tool, not a guarantee.
  ScratchInUse = hs::HS_SCRATCH_IN_USE,
  /// Unsupported CPU architecture.
  ///
  /// This error is returned when Hyperscan is able to detect that the current
  /// system does not support the required instruction set.
  ///
  /// At a minimum, Hyperscan requires Supplemental Streaming SIMD Extensions 3
  /// (SSSE3).
  ArchError = hs::HS_ARCH_ERROR,
  /// Provided buffer was too small.
  ///
  /// This error indicates that there was insufficient space in the buffer. The
  /// call should be repeated with a larger provided buffer.
  ///
  /// Note: in this situation, it is normal for the amount of space required to
  /// be returned in the same manner as the used space would have been
  /// returned if the call was successful.
  InsufficientSpace = hs::HS_INSUFFICIENT_SPACE,
  /// Unexpected internal error.
  ///
  /// This error indicates that there was unexpected matching behaviors. This
  /// could be related to invalid usage of stream and scratch space or invalid
  /// memory operations by users.
  #[num_enum(default)]
  UnknownError = hs::HS_UNKNOWN_ERROR,
}

impl HyperscanRuntimeError {
  #[inline]
  pub(crate) fn from_native(x: hs::hs_error_t) -> Result<(), Self> {
    static_assertions::const_assert_eq!(0, hs::HS_SUCCESS);
    if x == 0 {
      Ok(())
    } else {
      let s: Self = (x as i8).into();
      Err(s)
    }
  }

  #[cfg(feature = "compile")]
  #[inline]
  pub(crate) fn copy_from_native_compile_error(
    x: hs::hs_error_t,
    c: *mut hs::hs_compile_error,
  ) -> Result<(), HyperscanCompileError> {
    match Self::from_native(x) {
      Ok(()) => Ok(()),
      Err(Self::CompilerError) => {
        let e = CompileError::copy_from_native(unsafe { &mut *c }).unwrap();
        Err(HyperscanCompileError::Compile(e))
      },
      Err(e) => Err(e.into()),
    }
  }
}

#[derive(Debug, Display, Error)]
pub enum HyperscanFlagsError {
  /// A mode was created without BLOCK, STREAM, or VECTORED somehow.
  InvalidDbMode,
  /// SOM_LEFTMOST flag provided, but no SOM_HORIZON_* mode was specified.
  SomHorizonModeRequired,
}

/// compile error(@{expression}): {message}
#[cfg(feature = "compile")]
#[cfg_attr(docsrs, doc(cfg(feature = "compile")))]
#[derive(Debug, Display, Error)]
#[ignore_extra_doc_attributes]
pub struct CompileError {
  pub message: String,
  pub expression: ExpressionIndex,
}

#[cfg(feature = "compile")]
impl CompileError {
  #[inline]
  pub fn copy_from_native(x: &mut hs::hs_compile_error) -> Result<Self, HyperscanRuntimeError> {
    let hs::hs_compile_error {
      message,
      expression,
    } = x;
    assert!(!message.is_null());
    let ret = Self {
      message: unsafe { CStr::from_ptr(*message) }
        .to_string_lossy()
        .to_string(),
      expression: ExpressionIndex(*expression as c_uint),
    };
    HyperscanRuntimeError::from_native(unsafe { hs::hs_free_compile_error(x) })?;
    Ok(ret)
  }
}

#[cfg(feature = "compile")]
#[cfg_attr(docsrs, doc(cfg(feature = "compile")))]
#[derive(Debug, Display, Error)]
pub enum HyperscanCompileError {
  /// flags error: {0}
  Flags(#[from] HyperscanFlagsError),
  /// non-compilation error: {0}
  NonCompile(#[from] HyperscanRuntimeError),
  /// pattern compilation error: {0}
  Compile(#[from] CompileError),
  /// null byte in expression: {0}
  NullByte(#[from] NulError),
}

#[derive(Debug, Display, Error)]
pub enum CompressionError {
  /// other error: {0}
  Other(#[from] HyperscanRuntimeError),
  /// not enough space for {0} in buf
  NoSpace(usize),
}

#[derive(Debug, Display, Error)]
#[ignore_extra_doc_attributes]
pub enum HyperscanError {
  /// error from the hyperscan runtime: {0}
  Runtime(#[from] HyperscanRuntimeError),
  /// compile error: {0}
  #[cfg(feature = "compile")]
  #[cfg_attr(docsrs, doc(cfg(feature = "compile")))]
  Compile(#[from] HyperscanCompileError),
  /// error compressing stream: {0}
  Compression(#[from] CompressionError),
}

#[cfg(feature = "chimera")]
#[cfg_attr(docsrs, doc(cfg(feature = "chimera")))]
pub mod chimera {
  use super::*;

  use std::{os::raw::c_void, ptr};

  #[derive(
    Debug,
    Display,
    Error,
    Copy,
    Clone,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    num_enum::IntoPrimitive,
    num_enum::FromPrimitive,
  )]
  #[repr(i8)]
  #[ignore_extra_doc_attributes]
  pub enum ChimeraRuntimeError {
    /// A parameter passed to this function was invalid.
    Invalid = hs::CH_INVALID,
    /// A memory allocation failed.
    NoMem = hs::CH_NOMEM,
    /// The engine was terminated by callback.
    ///
    /// This return value indicates that the target buffer was partially
    /// scanned, but that the callback function requested that scanning
    /// cease after a match was located.
    ScanTerminated = hs::CH_SCAN_TERMINATED,
    /// The pattern compiler failed, and the @ref ch_compile_error_t should be
    /// inspected for more detail.
    CompilerError = hs::CH_COMPILER_ERROR,
    /// The given database was built for a different version of the Chimera
    /// matcher.
    DbVersionError = hs::CH_DB_VERSION_ERROR,
    /// The given database was built for a different platform (i.e., CPU type).
    DbPlatformError = hs::CH_DB_PLATFORM_ERROR,
    /// The given database was built for a different mode of operation.
    ///
    /// This error is returned when streaming calls are used with a
    /// non-streaming database and vice versa.
    DbModeError = hs::CH_DB_MODE_ERROR,
    /// A parameter passed to this function was not correctly aligned.
    BadAlign = hs::CH_BAD_ALIGN,
    /// The memory allocator did not correctly return memory suitably aligned
    /// for the largest representable data type on this platform.
    BadAlloc = hs::CH_BAD_ALLOC,
    /// The scratch region was already in use.
    ///
    /// This error is returned when Chimera is able to detect that the scratch
    /// region given is already in use by another Chimera API call.
    ///
    /// A separate scratch region, allocated with @ref ch_alloc_scratch() or
    /// @ref ch_clone_scratch(), is required for every concurrent caller of
    /// the Chimera API.
    ///
    /// For example, this error might be returned when @ref ch_scan() has been
    /// called inside a callback delivered by a currently-executing @ref
    /// ch_scan() call using the same scratch region.
    ///
    /// Note: Not all concurrent uses of scratch regions may be detected. This
    /// error is intended as a best-effort debugging tool, not a guarantee.
    ScratchInUse = hs::CH_SCRATCH_IN_USE,
    /// Unexpected internal error from Hyperscan.
    ///
    /// This error indicates that there was unexpected matching behaviors from
    /// Hyperscan. This could be related to invalid usage of scratch space or
    /// invalid memory operations by users.
    #[num_enum(default)]
    UnknownError = hs::CH_UNKNOWN_HS_ERROR,
    /// Returned when pcre_exec (called for some expressions internally from
    /// @ref ch_scan) failed due to a fatal error.
    FailInternal = hs::CH_FAIL_INTERNAL,
  }

  impl ChimeraRuntimeError {
    #[inline]
    pub(crate) fn from_native(x: hs::ch_error_t) -> Result<(), Self> {
      static_assertions::const_assert_eq!(0, hs::CH_SUCCESS);
      if x == 0 {
        Ok(())
      } else {
        let s: Self = (x as i8).into();
        Err(s)
      }
    }

    #[inline]
    pub(crate) fn copy_from_native_compile_error(
      x: hs::ch_error_t,
      c: *mut hs::ch_compile_error,
    ) -> Result<(), ChimeraCompileError> {
      match Self::from_native(x) {
        Ok(()) => Ok(()),
        Err(Self::CompilerError) => {
          let e = ChimeraInnerCompileError::copy_from_native(unsafe { &mut *c }).unwrap();
          Err(ChimeraCompileError::Compile(e))
        },
        Err(e) => Err(e.into()),
      }
    }
  }

  /// compile error(@{expression}): {message}
  #[derive(Debug, Display, Error)]
  pub struct ChimeraInnerCompileError {
    pub message: String,
    pub expression: ExpressionIndex,
  }

  impl ChimeraInnerCompileError {
    #[inline]
    pub fn copy_from_native(x: &mut hs::ch_compile_error) -> Result<Self, ChimeraRuntimeError> {
      let hs::ch_compile_error {
        message,
        expression,
      } = x;
      assert!(!message.is_null());
      let ret = Self {
        message: unsafe { CStr::from_ptr(*message) }
          .to_string_lossy()
          .to_string(),
        expression: ExpressionIndex(*expression as c_uint),
      };
      ChimeraRuntimeError::from_native(unsafe { hs::ch_free_compile_error(x) })?;
      Ok(ret)
    }
  }

  #[derive(Debug, Display, Error)]
  pub enum ChimeraCompileError {
    /// non-compilation error: {0}
    NonCompile(#[from] ChimeraRuntimeError),
    /// pattern compilation error: {0}
    Compile(#[from] ChimeraInnerCompileError),
    /// null byte in expression: {0}
    NullByte(#[from] NulError),
  }

  #[derive(
    Debug,
    Display,
    Error,
    Copy,
    Clone,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    num_enum::IntoPrimitive,
    num_enum::TryFromPrimitive,
  )]
  #[repr(u8)]
  pub enum ChimeraMatchErrorType {
    /// PCRE hits its match limit and reports PCRE_ERROR_MATCHLIMIT.
    MatchLimit = hs::CH_ERROR_MATCHLIMIT,
    /// PCRE hits its recursion limit and reports PCRE_ERROR_RECURSIONLIMIT.
    RecursionLimit = hs::CH_ERROR_RECURSIONLIMIT,
  }

  impl ChimeraMatchErrorType {
    #[inline]
    pub(crate) fn from_native(x: hs::ch_error_event_t) -> Self { (x as u8).try_into().unwrap() }
  }

  /// {error_type}@{id}(info={info:?})
  #[derive(Debug, Display, Error)]
  pub struct ChimeraMatchError {
    #[source]
    pub error_type: ChimeraMatchErrorType,
    pub id: ExpressionIndex,
    pub info: Option<ptr::NonNull<c_void>>,
  }

  unsafe impl Send for ChimeraMatchError {}

  #[derive(Debug, Display, Error)]
  pub enum ChimeraScanError {
    /// error from return value of ch_scan: {0}
    ReturnValue(#[from] ChimeraRuntimeError),
    /// streaming pcre error: {0}
    MatchError(#[from] ChimeraMatchError),
    /// join error: {0}
    Join(#[from] tokio::task::JoinError),
  }

  #[derive(Debug, Display, Error)]
  pub enum ChimeraError {
    /// error from chimera runtime: {0}
    Runtime(#[from] ChimeraRuntimeError),
    /// compile error: {0}
    Compile(#[from] ChimeraCompileError),
    /// error during chimera scan: {0}
    Scan(#[from] ChimeraScanError),
  }
}