github.com/shadialtarsha/go-pcre@v0.0.0-20220904164957-b4f1834ceecc/pcre.go (about)

     1  // Package pcre is a library that provides pcre2 regular expressions
     2  // in pure Go, allowing for features such as cross-compiling.
     3  //
     4  // The lib directory contains source code automatically translated from
     5  // pcre2's C source code for each supported architecture and/or OS.
     6  // This package wraps the automatically-translated source to provide a
     7  // safe interface as close to Go's regexp library as possible.
     8  package pcre
     9  
    10  import (
    11  	"runtime"
    12  	"sync"
    13  
    14  	"github.com/shadialtarsha/go-pcre/lib"
    15  
    16  	"modernc.org/libc"
    17  )
    18  
    19  // Version returns the version of pcre2 embedded in this library.
    20  func Version() string { return lib.DPACKAGE_VERSION }
    21  
    22  // Regexp represents a pcre2 regular expression
    23  type Regexp struct {
    24  	mtx  *sync.Mutex
    25  	expr string
    26  	re   uintptr
    27  	tls  *libc.TLS
    28  }
    29  
    30  // Compile runs CompileOpts with no options.
    31  //
    32  // Close() should be called on the returned expression
    33  // once it is no longer needed.
    34  func Compile(pattern string) (*Regexp, error) {
    35  	return CompileOpts(pattern, 0)
    36  }
    37  
    38  // CompileOpts compiles the provided pattern using the given options.
    39  //
    40  // Close() should be called on the returned expression
    41  // once it is no longer needed.
    42  func CompileOpts(pattern string, options CompileOption) (*Regexp, error) {
    43  	tls := libc.NewTLS()
    44  
    45  	// Get C string of pattern
    46  	cPattern, err := libc.CString(pattern)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	// Free the string when done
    51  	defer libc.Xfree(tls, cPattern)
    52  
    53  	// Allocate new error
    54  	cErr := allocError(tls)
    55  	// Free error when done
    56  	defer libc.Xfree(tls, cErr)
    57  
    58  	// Get error offsets
    59  	errPtr := addErrCodeOffset(cErr)
    60  	errOffsetPtr := addErrOffsetOffset(cErr)
    61  
    62  	// Convert pattern length to size_t type
    63  	cPatLen := lib.Tsize_t(len(pattern))
    64  
    65  	// Compile expression
    66  	r := lib.Xpcre2_compile_8(tls, cPattern, cPatLen, uint32(options), errPtr, errOffsetPtr, 0)
    67  	if r == 0 {
    68  		return nil, ptrToError(tls, cErr)
    69  	}
    70  
    71  	// Create regexp instance
    72  	regex := Regexp{
    73  		expr: pattern,
    74  		mtx:  &sync.Mutex{},
    75  		re:   r,
    76  		tls:  tls,
    77  	}
    78  
    79  	// Make sure resources are freed if GC collects the
    80  	// regular expression.
    81  	runtime.SetFinalizer(&regex, func(r *Regexp) error {
    82  		return r.Close()
    83  	})
    84  
    85  	return &regex, nil
    86  }
    87  
    88  // MustCompile compiles the given pattern and panics
    89  // if there was an error
    90  //
    91  // Close() should be called on the returned expression
    92  // once it is no longer needed.
    93  func MustCompile(pattern string) *Regexp {
    94  	rgx, err := Compile(pattern)
    95  	if err != nil {
    96  		panic(err)
    97  	}
    98  	return rgx
    99  }
   100  
   101  // MustCompileOpts compiles the given pattern with the given
   102  // options and panics if there was an error.
   103  //
   104  // Close() should be called on the returned expression
   105  // once it is no longer needed.
   106  func MustCompileOpts(pattern string, options CompileOption) *Regexp {
   107  	rgx, err := CompileOpts(pattern, options)
   108  	if err != nil {
   109  		panic(err)
   110  	}
   111  	return rgx
   112  }
   113  
   114  // Close frees resources used by the regular expression.
   115  func (r *Regexp) Close() error {
   116  	if r == nil {
   117  		return nil
   118  	}
   119  
   120  	// Close thread-local storage
   121  	defer r.tls.Close()
   122  
   123  	// Free the compiled code
   124  	lib.Xpcre2_code_free_8(r.tls, r.re)
   125  	// Set regular expression to null
   126  	r.re = 0
   127  
   128  	return nil
   129  }