Skip to content

Commit

Permalink
Merge pull request #38 from flier/features/chimera
Browse files Browse the repository at this point in the history
add chimera
  • Loading branch information
flier authored Apr 11, 2022
2 parents 8951c06 + 1412847 commit ebed1d6
Show file tree
Hide file tree
Showing 99 changed files with 8,860 additions and 3,602 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.github
examples
*.tar.gz
173 changes: 154 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
name: Continuous integration

on: [push, pull_request]
on:
push:
branches:
- master
paths-ignore:
- "**.md"
pull_request:
paths-ignore:
- "**.md"

defaults:
run:
Expand All @@ -11,46 +19,170 @@ jobs:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, macos-latest]
go: [1.17.x, 1.16.x, 1.15.x, 1.14.x, 1.13.x]
name: Go ${{ matrix.go }} tests @ ${{ matrix.os }}
go: [1.18.x, 1.17.x, 1.16.x, 1.15.x]
name: Go ${{ matrix.go }} tests @ ${{ matrix.os }} for hyperscan ${{ matrix.hyperscan }}
runs-on: ${{ matrix.os }}
steps:
- name: Install Linux dependencies
- name: Install Linux dependencies for testing libraries
if: startsWith(matrix.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -yq libhyperscan-dev libpcap-dev
- name: Install MacOS dependencies
- name: Install MacOS dependencies for testing libraries
if: startsWith(matrix.os, 'macos-')
run: |
brew install pkg-config hyperscan libpcap
brew install hyperscan pkg-config libpcap
- uses: actions/checkout@v2

- name: Install Golang ${{ matrix.go }}
id: install-golang
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
- run: go version

- name: Checkout code
uses: actions/checkout@v2

- name: Cache Golang modules
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
path: |
~/.cache/go-build
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Test v4 API
- name: Test Hyperscan v4 API
if: matrix.os == 'ubuntu-18.04'
run: go test -v -tags hyperscan_v4 ./...
run: |
go test -v -tags hyperscan_v4 ./internal/hs/... ./hyperscan/...
go test -race -v -tags hyperscan_v4 ./internal/hs/... ./hyperscan/...
- name: Test Hyperscan v5 API
if: matrix.os != 'ubuntu-18.04'
run: |
go test -v ./internal/hs/... ./hyperscan/...
go test -race -v ./internal/hs/... ./hyperscan/...
build-and-test:
strategy:
matrix:
include:
- os: macos-latest
hyperscan: 5.4.0
pcre: 8.45
build_flags: -tags hyperscan_v54,chimera
chimera: true
- os: macos-latest
hyperscan: 5.2.1
pcre: 8.45
build_flags: -tags hyperscan_v52,chimera
chimera: true
- os: macos-latest
hyperscan: 5.1.1
pcre: 8.45
build_flags: -tags chimera
chimera: true
- os: ubuntu-20.04
hyperscan: 5.4.0
pcre: 8.45
build_flags: -tags hyperscan_v54,chimera
chimera: true
coverage: true
- os: ubuntu-20.04
hyperscan: 5.2.1
pcre: 8.45
build_flags: -tags hyperscan_v52,chimera
chimera: true
- os: ubuntu-20.04
hyperscan: 5.1.1
pcre: 8.45
build_flags: -tags chimera
chimera: true
- os: ubuntu-18.04
hyperscan: 4.7.0
pcre: 8.41
build_flags: -tags hyperscan_v4
name: Go ${{ matrix.go }} tests @ ${{ matrix.os }} for hyperscan ${{ matrix.hyperscan }}
runs-on: ${{ matrix.os }}
env:
PKG_CONFIG_PATH: ${{ github.workspace }}/dist/lib/pkgconfig
CGO_CFLAGS: -I${{ github.workspace }}/dist/include/hs
CGO_LDFLAGS: -L${{ github.workspace }}/dist/lib
steps:
- name: Install Linux dependencies
if: startsWith(matrix.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -yq build-essential ca-certificates cmake libboost-dev libbz2-dev libpcap-dev \
ninja-build pkg-config python2.7 ragel wget zlib1g-dev tree
- name: Install MacOS dependencies
if: startsWith(matrix.os, 'macos-')
run: |
brew update
brew install pkg-config libpcap ragel cmake boost ninja lzlib wget tree
- name: Test v5 API
if: matrix.os == 'ubuntu-20.04' || matrix.os == 'macos-latest'
run: go test -v ./...
- uses: actions/checkout@v2

- name: Install Hyperscan ${{ matrix.hyperscan }} with PCRE ${{ matrix.pcre }}
if: steps.cache-hyperscan.outputs.cache-hit == false
uses: flier/install-hyperscan@main
with:
hyperscan_version: ${{ matrix.hyperscan }}
pcre_version: ${{ matrix.pcre }}
build_static_lib: on
src_dir: ${{ runner.temp }}/hyperscan/
install_prefix: ${{ github.workspace }}/dist/
cache_key: ${{ runner.os }}-build-hyperscan-${{ matrix.hyperscan }}-pcre-${{ matrix.pcre }}

- name: Upload Hyperscan ${{ matrix.hyperscan }}
if: steps.cache-hyperscan.outputs.cache-hit == false
uses: actions/upload-artifact@v2
with:
name: hyperscan-${{ matrix.hyperscan }}-pcre-${{ matrix.pcre }}
path: ${{ github.workspace }}/dist/
if-no-files-found: error

- name: Check cached Hyperscan ${{ matrix.hyperscan}} with PCRE ${{ matrix.pcre }}
if: steps.cache-hyperscan.outputs.cache-hit
run: |
tree ${{ github.workspace }}/dist
- name: Install Golang ${{ matrix.go }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}

- name: Cache Golang modules
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Test Hyperscan API
run: |
go test -v ${{ matrix.build_flags }} ./internal/hs/... ./hyperscan/...
go test -race -v ${{ matrix.build_flags }} ./internal/hs/... ./hyperscan/...
- name: Test Chimera API
if: matrix.chimera
run: |
go test -v ${{ matrix.build_flags }} ./internal/ch/... ./chimera/...
go test -race -v ${{ matrix.build_flags }} ./internal/ch/... ./chimera/...
- name: Run and upload coverage to Codecov
if: matrix.coverage
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: |
go test -race -coverprofile=coverage.out -covermode=atomic ./...
bash <(curl -s https://codecov.io/bash)
golangci:
name: lint
Expand All @@ -60,9 +192,12 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -yq libhyperscan-dev libpcap-dev
- uses: actions/setup-go@v2
- uses: actions/checkout@v2

- name: golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v3
with:
version: latest
working-directory: hyperscan
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ _testmain.go
*.exe
*.test
*.prof

*.out
61 changes: 61 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
ARG UBUNTU_VERSION=20.04

FROM ubuntu:${UBUNTU_VERSION}

ARG GO_VERSION=1.17.1
ARG HYPERSCAN_VERSION=5.4.0
ARG PCRE_VERSION=8.45

# Install dependencies

ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
cmake \
libboost-dev \
libbz2-dev \
libpcap-dev \
ninja-build \
pkg-config \
python2.7 \
ragel \
wget \
zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*

# Install golang

RUN wget https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz -O /go${GO_VERSION}.tar.gz && \
rm -rf /usr/local/go && \
tar -C /usr/local -xzf /go${GO_VERSION}.tar.gz && \
rm /go${GO_VERSION}.tar.gz

ENV PATH=/usr/local/go/bin:${PATH}

# Download Hyperscan

ENV HYPERSCAN_DIR=/hyperscan

RUN wget https://github.com/intel/hyperscan/archive/refs/tags/v${HYPERSCAN_VERSION}.tar.gz -O /hyperscan-${HYPERSCAN_VERSION}.tar.gz && \
mkdir ${HYPERSCAN_DIR} && tar xf /hyperscan-${HYPERSCAN_VERSION}.tar.gz -C ${HYPERSCAN_DIR} --strip-components=1 && rm /hyperscan-${HYPERSCAN_VERSION}.tar.gz
RUN wget https://sourceforge.net/projects/pcre/files/pcre/${PCRE_VERSION}/pcre-${PCRE_VERSION}.tar.gz/download -O /pcre-${PCRE_VERSION}.tar.gz && \
mkdir ${HYPERSCAN_DIR}/pcre && tar xf /pcre-${PCRE_VERSION}.tar.gz -C ${HYPERSCAN_DIR}/pcre --strip-components=1 && rm /pcre-${PCRE_VERSION}.tar.gz

# Install Hyperscan

ENV INSTALL_DIR=/usr/local

RUN mkdir ${HYPERSCAN_DIR}/build && cd ${HYPERSCAN_DIR}/build && \
cmake -G Ninja -DBUILD_STATIC_LIBS=on -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} ${HYPERSCAN_DIR} && \
ninja && ninja install && mv ${HYPERSCAN_DIR}/build/lib/lib*.a ${INSTALL_DIR}/lib/ && cd / && rm -rf ${HYPERSCAN_DIR}

ENV PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig

# Add gohs code

ADD . /gohs/

WORKDIR /gohs
ENTRYPOINT ["/usr/local/go/bin/go"]
CMD ["test", "./..."]
35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,44 @@
# gohs [![Continuous integration](https://github.com/flier/gohs/actions/workflows/ci.yml/badge.svg?)](https://github.com/flier/gohs/actions/workflows/ci.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/flier/gohs?)](https://goreportcard.com/report/github.com/flier/gohs) [![Go Reference](https://pkg.go.dev/badge/github.com/flier/gohs/hyperscan.svg)](https://pkg.go.dev/github.com/flier/gohs/hyperscan) [![Apache](https://img.shields.io/badge/license-Apache-blue.svg)](https://github.com/flier/gohs/blob/master/LICENSE-APACHE) [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/flier/gohs/blob/master/LICENSE-MIT)
# gohs [![Continuous integration](https://github.com/flier/gohs/actions/workflows/ci.yml/badge.svg?)](https://github.com/flier/gohs/actions/workflows/ci.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/flier/gohs?)](https://goreportcard.com/report/github.com/flier/gohs) [![codecov](https://codecov.io/gh/flier/gohs/branch/master/graph/badge.svg?token=F5CLCxpJGM)](https://codecov.io/gh/flier/gohs) [![Apache](https://img.shields.io/badge/license-Apache-blue.svg)](https://github.com/flier/gohs/blob/master/LICENSE-APACHE) [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/flier/gohs/blob/master/LICENSE-MIT)

Golang binding for Intel's HyperScan regex matching library: [hyperscan.io](https://www.hyperscan.io/)

## Build
## Hyperscan [![Go Reference](https://pkg.go.dev/badge/github.com/flier/gohs/hyperscan.svg)](https://pkg.go.dev/github.com/flier/gohs/hyperscan)

**Note:** `gohs` will use Hyperscan v5 API by default, you can also build for Hyperscan v4 with `hyperscan_v4` tag.
Hyperscan is a software regular expression matching engine designed with high performance and flexibility in mind. It is implemented as a library that exposes a straightforward C API.

### Build

`gohs` does not enable the latest api of Hyperscan v5.4 by default, if you want to use it please pass build tags `hyperscan_v54`.

```bash
go get -u -tags hyperscan_v54 github.com/flier/gohs/hyperscan
```

`gohs` will use Hyperscan v5 API by default, you can also build for Hyperscan v4 with `hyperscan_v4` tag.

```bash
go get -u -tags hyperscan_v4 github.com/flier/gohs/hyperscan
```

## Chimera [![Go Reference](https://pkg.go.dev/badge/github.com/flier/gohs/chimera.svg)](https://pkg.go.dev/github.com/flier/gohs/chimera)

Chimera is a software regular expression matching engine that is a hybrid of Hyperscan and PCRE. The design goals of Chimera are to fully support PCRE syntax as well as to take advantage of the high performance nature of Hyperscan.

### Build

It is recommended to compile and link Chimera using static libraries.

```bash
$ mkdir build && cd build
$ cmake .. -G Ninja -DBUILD_STATIC_LIBS=on
$ ninja && ninja install
$ go get -u -tags chimera github.com/flier/gohs/hyperscan
```

### Note

You need to download the PCRE library source code to build Chimera, see [Chimera Requirements](https://intel.github.io/hyperscan/dev-reference/chimera.html#requirements) for more details

## License

This project is licensed under either of Apache License ([LICENSE-APACHE](LICENSE-APACHE)) or MIT license ([LICENSE-MIT](LICENSE-MIT)) at your option.
Expand Down
49 changes: 49 additions & 0 deletions bench/go/chimera_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//go:build chimera
// +build chimera

package scan_test

import (
"strings"
"testing"

"github.com/flier/gohs/chimera"
)

func BenchmarkChimeraBlockScan(b *testing.B) {
isRaceBuilder := strings.HasSuffix(testenv(), "-race")

for _, data := range benchData {
p := chimera.NewPattern(data.re, chimera.MultiLine)
db, err := chimera.NewBlockDatabase(p)
if err != nil {
b.Fatalf("compile pattern %s: `%s`, %s", data.name, data.re, err)
}

s, err := chimera.NewScratch(db)
if err != nil {
b.Fatalf("create scratch, %s", err)
}

m := chimera.HandlerFunc(func(id uint, from, to uint64, flags uint,
captured []*chimera.Capture, context interface{},
) chimera.Callback {
return chimera.Terminate
})

for _, size := range benchSizes {
if (isRaceBuilder || testing.Short()) && size.n > 1<<10 {
continue
}
t := makeText(size.n)
b.Run(data.name+"/"+size.name, func(b *testing.B) {
b.SetBytes(int64(len(t)))
for i := 0; i < b.N; i++ {
if err = db.Scan(t, s, m, nil); err != nil {
b.Fatalf("match, %s", err)
}
}
})
}
}
}
Loading

0 comments on commit ebed1d6

Please sign in to comment.