Compare commits

...

14 Commits

Author SHA1 Message Date
Viktor Scharf
da3a893634 getting all spaces using beta graph endpoint 2025-04-30 12:25:55 +02:00
Viktor Scharf
7bb95d057a check that created user has only one personal space 2025-04-29 16:26:39 +02:00
Ralf Haferkamp
a97f5e4c2e Merge pull request #740 from opencloud-eu/dependabot/npm_and_yarn/services/idp/css-loader-7.1.2
build(deps-dev): bump css-loader from 5.2.7 to 7.1.2 in /services/idp
2025-04-29 14:56:59 +02:00
Michael Barz
cc303d14f4 Supported production setup with Keycloak and LDAP (#720)
* feat: modify .env file to make the needed changes for keycloak and ldap

* style: remove default values from yaml
2025-04-29 14:29:33 +02:00
dependabot[bot]
467cbc27a1 build(deps-dev): bump css-loader from 5.2.7 to 7.1.2 in /services/idp
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 5.2.7 to 7.1.2.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v5.2.7...v7.1.2)

---
updated-dependencies:
- dependency-name: css-loader
  dependency-version: 7.1.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 10:32:29 +00:00
Ralf Haferkamp
93681ab2ff Merge pull request #741 from opencloud-eu/dependabot/npm_and_yarn/services/idp/react-i18next-15.5.1
build(deps): bump react-i18next from 15.1.1 to 15.5.1 in /services/idp
2025-04-29 12:29:56 +02:00
Jörn Friedrich Dreyer
bf6d07352f Merge pull request #748 from opencloud-eu/mockery-polish
fix mockery warnings
2025-04-29 11:41:25 +02:00
Ralf Haferkamp
99d69b4707 Merge pull request #743 from opencloud-eu/dependabot/go_modules/github.com/blevesearch/bleve/v2-2.5.0
build(deps): bump github.com/blevesearch/bleve/v2 from 2.4.4 to 2.5.0
2025-04-29 11:40:00 +02:00
Jörn Friedrich Dreyer
3c38a3e3ed fix mockery warnings
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2025-04-29 11:01:17 +02:00
dependabot[bot]
0c84ba3ad2 build(deps): bump github.com/blevesearch/bleve/v2 from 2.4.4 to 2.5.0
Bumps [github.com/blevesearch/bleve/v2](https://github.com/blevesearch/bleve) from 2.4.4 to 2.5.0.
- [Release notes](https://github.com/blevesearch/bleve/releases)
- [Commits](https://github.com/blevesearch/bleve/compare/v2.4.4...v2.5.0)

---
updated-dependencies:
- dependency-name: github.com/blevesearch/bleve/v2
  dependency-version: 2.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 08:41:09 +00:00
Ralf Haferkamp
3d9de26f1c Merge pull request #744 from opencloud-eu/dependabot/go_modules/github.com/gabriel-vasile/mimetype-1.4.9
build(deps): bump github.com/gabriel-vasile/mimetype from 1.4.8 to 1.4.9
2025-04-29 10:38:40 +02:00
Alex
3904dbeb19 fix: idp login logo url exceeds logo (#742) 2025-04-29 09:38:07 +02:00
dependabot[bot]
f0b304d492 build(deps): bump github.com/gabriel-vasile/mimetype from 1.4.8 to 1.4.9
Bumps [github.com/gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) from 1.4.8 to 1.4.9.
- [Release notes](https://github.com/gabriel-vasile/mimetype/releases)
- [Commits](https://github.com/gabriel-vasile/mimetype/compare/v1.4.8...v1.4.9)

---
updated-dependencies:
- dependency-name: github.com/gabriel-vasile/mimetype
  dependency-version: 1.4.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-28 16:10:02 +00:00
dependabot[bot]
2f820e4756 build(deps): bump react-i18next from 15.1.1 to 15.5.1 in /services/idp
Bumps [react-i18next](https://github.com/i18next/react-i18next) from 15.1.1 to 15.5.1.
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v15.1.1...v15.5.1)

---
updated-dependencies:
- dependency-name: react-i18next
  dependency-version: 15.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-28 16:04:19 +00:00
308 changed files with 20966 additions and 5271 deletions

View File

@@ -260,12 +260,12 @@ COMPOSE_PATH_SEPARATOR=:
# configuring an external LDAP server based on other products like Microsoft Active Directory or other LDAP servers.
#
# Note: the leading colon is required to enable the service.
LDAP=:ldap.yml
#LDAP=:ldap.yml
# Password of LDAP user "cn=admin,dc=opencloud,dc=eu". Defaults to "admin"
LDAP_ADMIN_PASSWORD=
# LDAP manager
# login with uid ldapadmin and password
LDAP_MANAGER=:../shared/config/ldap/docker-compose.yml
#LDAP_MANAGER=:../shared/config/ldap/docker-compose.yml
# LDAP manager domain. Defaults to "ldap.opencloud.test"
LDAP_MANAGER_DOMAIN=
@@ -282,7 +282,7 @@ LDAP_MANAGER_DOMAIN=
# 2. Shared User Directory: Users are created in Keycloak and can be used in OpenCloud immediately
# because the LDAP server is connected to both Keycloak and OpenCloud.
# Note: the leading colon is required to enable the service.
KEYCLOAK=:keycloak.yml
#KEYCLOAK=:keycloak.yml
# Domain for Keycloak. Defaults to "keycloak.opencloud.test".
KEYCLOAK_DOMAIN=
# Realm which to be used with OpenCloud. Defaults to "OpenCloud"
@@ -292,7 +292,7 @@ KEYCLOAK_ADMIN_USER=
# Admin user login password. Defaults to "admin"
KEYCLOAK_ADMIN_PASSWORD=
# Autoprovisioning mode. Defaults to "true"
KEYCLOAK_AUTOPROVISIONING=:keycloak-autoprovisioning.yml
#KEYCLOAK_AUTOPROVISIONING=:keycloak-autoprovisioning.yml
## IMPORTANT ##
# This MUST be the last line as it assembles the supplemental compose files to be used.

View File

File diff suppressed because it is too large Load Diff

View File

@@ -12,18 +12,16 @@ services:
OC_LDAP_BIND_DN: "cn=admin,dc=opencloud,dc=eu"
OC_LDAP_BIND_PASSWORD: ${LDAP_ADMIN_PASSWORD:-admin}
OC_LDAP_GROUP_BASE_DN: "ou=groups,dc=opencloud,dc=eu"
OC_LDAP_GROUP_FILTER: "(objectclass=opencloudobject)"
OC_LDAP_GROUP_OBJECTCLASS: "groupOfNames"
OC_LDAP_GROUP_SCHEMA_ID: "entryUUID"
OC_LDAP_USER_BASE_DN: "ou=users,dc=opencloud,dc=eu"
OC_LDAP_USER_FILTER: "(objectclass=openclouduser)"
OC_LDAP_USER_OBJECTCLASS: "inetOrgPerson"
LDAP_LOGIN_ATTRIBUTES: "uid"
OC_ADMIN_USER_ID: "f7fc96f6-ceb4-4387-bd69-07a6d7992973"
IDP_LDAP_LOGIN_ATTRIBUTE: "uid"
IDP_LDAP_UUID_ATTRIBUTE: "openclouduuid"
IDP_LDAP_UUID_ATTRIBUTE_TYPE: binary
GRAPH_LDAP_SERVER_WRITE_ENABLED: "true" # assuming the external ldap is writable
OC_LDAP_USER_FILTER: "(objectclass=inetOrgPerson)"
OC_LDAP_USER_SCHEMA_ID: "entryUUID"
OC_LDAP_DISABLE_USER_MECHANISM: "none"
GRAPH_LDAP_SERVER_UUID: "true"
GRAPH_LDAP_GROUP_CREATE_BASE_DN: "ou=custom,ou=groups,dc=opencloud,dc=eu"
GRAPH_LDAP_REFINT_ENABLED: "true" # osixia has refint enabled.
FRONTEND_READONLY_USER_ATTRIBUTES: "user.onPremisesSamAccountName,user.displayName,user.mail,user.passwordProfile,user.accountEnabled,user.appRoleAssignments"
OC_LDAP_SERVER_WRITE_ENABLED: "false" # assuming the external ldap is not writable
# OC_RUN_SERVICES specifies to start all services except glauth, idm and accounts. These are replaced by external services
OC_EXCLUDE_RUN_SERVICES: idm
@@ -46,7 +44,6 @@ services:
- "127.0.0.1:636:1636"
volumes:
- ./config/ldap/ldif:/ldifs
- ../shared/config/ldap/schemas/10_opencloud_schema.ldif:/schemas/10_opencloud_schema.ldif
- ./config/ldap/docker-entrypoint-override.sh:/opt/bitnami/scripts/openldap/docker-entrypoint-override.sh
- ldap-certs:/opt/bitnami/openldap/share
- ldap-data:/bitnami/openldap

28
go.mod
View File

@@ -11,7 +11,7 @@ require (
github.com/Nerzal/gocloak/v13 v13.9.0
github.com/bbalet/stopwords v1.0.0
github.com/beevik/etree v1.5.0
github.com/blevesearch/bleve/v2 v2.4.4
github.com/blevesearch/bleve/v2 v2.5.0
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/coreos/go-oidc/v3 v3.14.1
github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1
@@ -19,7 +19,7 @@ require (
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/egirna/icap-client v0.1.1
github.com/gabriel-vasile/mimetype v1.4.8
github.com/gabriel-vasile/mimetype v1.4.9
github.com/ggwhite/go-masker v1.1.0
github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/render v1.0.3
@@ -121,7 +121,7 @@ require (
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/RoaringBitmap/roaring v1.9.3 // indirect
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/agnivade/levenshtein v1.2.1 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/alexedwards/argon2id v1.0.0 // indirect
@@ -131,24 +131,24 @@ require (
github.com/aws/aws-sdk-go v1.55.6 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/bits-and-blooms/bitset v1.12.0 // indirect
github.com/blevesearch/bleve_index_api v1.1.12 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blevesearch/bleve_index_api v1.2.7 // indirect
github.com/blevesearch/geo v0.1.20 // indirect
github.com/blevesearch/go-faiss v1.0.24 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.9 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.0.10 // indirect
github.com/blevesearch/zapx/v11 v11.3.10 // indirect
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b // indirect
github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.1 // indirect
github.com/blevesearch/zapx/v12 v12.4.1 // indirect
github.com/blevesearch/zapx/v13 v13.4.1 // indirect
github.com/blevesearch/zapx/v14 v14.4.1 // indirect
github.com/blevesearch/zapx/v15 v15.4.1 // indirect
github.com/blevesearch/zapx/v16 v16.2.2 // indirect
github.com/bluele/gcache v0.0.2 // indirect
github.com/bombsimon/logrusr/v3 v3.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect

56
go.sum
View File

@@ -87,8 +87,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
@@ -142,45 +142,46 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bits-and-blooms/bitset v1.12.0 h1:U/q1fAF7xXRhFCrhROzIfffYnu+dlS38vCZtmFVPHmA=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blevesearch/bleve/v2 v2.4.4 h1:RwwLGjUm54SwyyykbrZs4vc1qjzYic4ZnAnY9TwNl60=
github.com/blevesearch/bleve/v2 v2.4.4/go.mod h1:fa2Eo6DP7JR+dMFpQe+WiZXINKSunh7WBtlDGbolKXk=
github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY=
github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
github.com/blevesearch/bleve/v2 v2.5.0 h1:HzYqBy/5/M9Ul9ESEmXzN/3Jl7YpmWBdHM/+zzv/3k4=
github.com/blevesearch/bleve/v2 v2.5.0/go.mod h1:PcJzTPnEynO15dCf9isxOga7YFRa/cMSsbnRwnszXUk=
github.com/blevesearch/bleve_index_api v1.2.7 h1:c8r9vmbaYQroAMSGag7zq5gEVPiuXrUQDqfnj7uYZSY=
github.com/blevesearch/bleve_index_api v1.2.7/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
github.com/blevesearch/go-faiss v1.0.24 h1:K79IvKjoKHdi7FdiXEsAhxpMuns0x4fM0BO93bW5jLI=
github.com/blevesearch/go-faiss v1.0.24/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U=
github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0=
github.com/blevesearch/scorch_segment_api/v2 v2.3.9 h1:X6nJXnNHl7nasXW+U6y2Ns2Aw8F9STszkYkyBfQ+p0o=
github.com/blevesearch/scorch_segment_api/v2 v2.3.9/go.mod h1:IrzspZlVjhf4X29oJiEhBxEteTqOY9RlYlk1lCmYHr4=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A=
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI=
github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk=
github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ=
github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s=
github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8=
github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE=
github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b h1:ju9Az5YgrzCeK3M1QwvZIpxYhChkXp7/L0RhDYsxXoE=
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b/go.mod h1:BlrYNpOu4BvVRslmIG+rLtKhmjIaRhIbG8sb9scGTwI=
github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
github.com/blevesearch/zapx/v11 v11.4.1 h1:qFCPlFbsEdwbbckJkysptSQOsHn4s6ZOHL5GMAIAVHA=
github.com/blevesearch/zapx/v11 v11.4.1/go.mod h1:qNOGxIqdPC1MXauJCD9HBG487PxviTUUbmChFOAosGs=
github.com/blevesearch/zapx/v12 v12.4.1 h1:K77bhypII60a4v8mwvav7r4IxWA8qxhNjgF9xGdb9eQ=
github.com/blevesearch/zapx/v12 v12.4.1/go.mod h1:QRPrlPOzAxBNMI0MkgdD+xsTqx65zbuPr3Ko4Re49II=
github.com/blevesearch/zapx/v13 v13.4.1 h1:EnkEMZFUK0lsW/jOJJF2xOcp+W8TjEsyeN5BeAZEYYE=
github.com/blevesearch/zapx/v13 v13.4.1/go.mod h1:e6duBMlCvgbH9rkzNMnUa9hRI9F7ri2BRcHfphcmGn8=
github.com/blevesearch/zapx/v14 v14.4.1 h1:G47kGCshknBZzZAtjcnIAMn3oNx8XBLxp8DMq18ogyE=
github.com/blevesearch/zapx/v14 v14.4.1/go.mod h1:O7sDxiaL2r2PnCXbhh1Bvm7b4sP+jp4unE9DDPWGoms=
github.com/blevesearch/zapx/v15 v15.4.1 h1:B5IoTMUCEzFdc9FSQbhVOxAY+BO17c05866fNruiI7g=
github.com/blevesearch/zapx/v15 v15.4.1/go.mod h1:b/MreHjYeQoLjyY2+UaM0hGZZUajEbE0xhnr1A2/Q6Y=
github.com/blevesearch/zapx/v16 v16.2.2 h1:MifKJVRTEhMTgSlle2bDRTb39BGc9jXFRLPZc6r0Rzk=
github.com/blevesearch/zapx/v16 v16.2.2/go.mod h1:B9Pk4G1CqtErgQV9DyCSA9Lb7WZe4olYfGw7fVDZ4sk=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
@@ -317,8 +318,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U=
github.com/gdexlab/go-render v1.0.1/go.mod h1:wRi5nW2qfjiGj4mPukH4UV0IknS1cHD4VgFTmJX5JzM=
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
@@ -1663,6 +1664,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
dir: "{{.PackageName}}/mocks"
mockname: "{{.InterfaceName}}"

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
dir: "{{.InterfaceDir}}/mocks"
mockname: "{{.InterfaceName}}"

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
dir: "pkg/{{.PackageName}}/mocks"
mockname: "{{.InterfaceName}}"

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
mockname: "{{.InterfaceName}}"
outpkg: "mocks"

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks
@@ -8,11 +8,11 @@ import (
)
// Selectable is an autogenerated mock type for the Selectable type
type Selectable[T interface{}] struct {
type Selectable[T any] struct {
mock.Mock
}
type Selectable_Expecter[T interface{}] struct {
type Selectable_Expecter[T any] struct {
mock *mock.Mock
}
@@ -57,7 +57,7 @@ func (_m *Selectable[T]) Next(opts ...pool.Option) (T, error) {
}
// Selectable_Next_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Next'
type Selectable_Next_Call[T interface{}] struct {
type Selectable_Next_Call[T any] struct {
*mock.Call
}
@@ -93,7 +93,7 @@ func (_c *Selectable_Next_Call[T]) RunAndReturn(run func(...pool.Option) (T, err
// NewSelectable creates a new instance of Selectable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewSelectable[T interface{}](t interface {
func NewSelectable[T any](t interface {
mock.TestingT
Cleanup(func())
}) *Selectable[T] {

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
mockname: "{{.InterfaceName}}"
outpkg: "mocks"

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks
@@ -8,11 +8,11 @@ import (
)
// Selectable is an autogenerated mock type for the Selectable type
type Selectable[T interface{}] struct {
type Selectable[T any] struct {
mock.Mock
}
type Selectable_Expecter[T interface{}] struct {
type Selectable_Expecter[T any] struct {
mock *mock.Mock
}
@@ -57,7 +57,7 @@ func (_m *Selectable[T]) Next(opts ...pool.Option) (T, error) {
}
// Selectable_Next_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Next'
type Selectable_Next_Call[T interface{}] struct {
type Selectable_Next_Call[T any] struct {
*mock.Call
}
@@ -93,7 +93,7 @@ func (_c *Selectable_Next_Call[T]) RunAndReturn(run func(...pool.Option) (T, err
// NewSelectable creates a new instance of Selectable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewSelectable[T interface{}](t interface {
func NewSelectable[T any](t interface {
mock.TestingT
Cleanup(func())
}) *Selectable[T] {

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -94,7 +94,7 @@
"react": "^17.0.2",
"react-app-polyfill": "^3.0.0",
"react-dom": "^17.0.2",
"react-i18next": "^15.1.1",
"react-i18next": "^15.5.1",
"react-redux": "^8.1.3",
"react-router": "^5.3.4",
"react-router-dom": "5.2.1",
@@ -114,7 +114,7 @@
"babel-preset-react-app": "^10.1.0",
"case-sensitive-paths-webpack-plugin": "2.4.0",
"cldr": "^7.5.0",
"css-loader": "5.2.7",
"css-loader": "7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0",
"dotenv": "16.4.7",
"dotenv-expand": "10.0.0",

View File

@@ -81,8 +81,8 @@ importers:
specifier: ^17.0.2
version: 17.0.2(react@17.0.2)
react-i18next:
specifier: ^15.1.1
version: 15.1.1(i18next@23.16.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
specifier: ^15.5.1
version: 15.5.1(i18next@23.16.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.3)
react-redux:
specifier: ^8.1.3
version: 8.1.3(@types/react-dom@17.0.25)(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(redux@4.2.1)
@@ -136,8 +136,8 @@ importers:
specifier: ^7.5.0
version: 7.5.0
css-loader:
specifier: 5.2.7
version: 5.2.7(webpack@5.99.6)
specifier: 7.1.2
version: 7.1.2(webpack@5.99.6)
css-minimizer-webpack-plugin:
specifier: ^7.0.0
version: 7.0.0(webpack@5.99.6)
@@ -984,10 +984,6 @@ packages:
resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.25.7':
resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.26.7':
resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==}
engines: {node: '>=6.9.0'}
@@ -2586,11 +2582,17 @@ packages:
peerDependencies:
postcss: ^8.4
css-loader@5.2.7:
resolution: {integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==}
engines: {node: '>= 10.13.0'}
css-loader@7.1.2:
resolution: {integrity: sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==}
engines: {node: '>= 18.12.0'}
peerDependencies:
webpack: ^4.27.0 || ^5.0.0
'@rspack/core': 0.x || 1.x
webpack: ^5.27.0
peerDependenciesMeta:
'@rspack/core':
optional: true
webpack:
optional: true
css-minimizer-webpack-plugin@7.0.0:
resolution: {integrity: sha512-niy66jxsQHqO+EYbhPuIhqRQ1mNcNVUHrMnkzzir9kFOERJUaQDDRhh7dKDz33kBpkWMF9M8Vx0QlDbc5AHOsw==}
@@ -4437,6 +4439,11 @@ packages:
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -4920,14 +4927,14 @@ packages:
peerDependencies:
postcss: ^8.1.0
postcss-modules-local-by-default@4.0.5:
resolution: {integrity: sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==}
postcss-modules-local-by-default@4.2.0:
resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
postcss-modules-scope@3.2.0:
resolution: {integrity: sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==}
postcss-modules-scope@3.2.1:
resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==}
engines: {node: ^10 || ^12 || >= 14}
peerDependencies:
postcss: ^8.1.0
@@ -5075,10 +5082,6 @@ packages:
peerDependencies:
postcss: ^8.4
postcss-selector-parser@6.1.1:
resolution: {integrity: sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==}
engines: {node: '>=4'}
postcss-selector-parser@6.1.2:
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
engines: {node: '>=4'}
@@ -5106,6 +5109,10 @@ packages:
resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==}
engines: {node: ^10 || ^12 || >=14}
postcss@8.5.3:
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -5205,18 +5212,21 @@ packages:
react-error-overlay@6.0.11:
resolution: {integrity: sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==}
react-i18next@15.1.1:
resolution: {integrity: sha512-R/Vg9wIli2P3FfeI8o1eNJUJue5LWpFsQePCHdQDmX0Co3zkr6kdT8gAseb/yGeWbNz1Txc4bKDQuZYsC0kQfw==}
react-i18next@15.5.1:
resolution: {integrity: sha512-C8RZ7N7H0L+flitiX6ASjq9p5puVJU1Z8VyL3OgM/QOMRf40BMZX+5TkpxzZVcTmOLPX5zlti4InEX5pFyiVeA==}
peerDependencies:
i18next: '>= 23.2.3'
react: '>= 16.8.0'
react-dom: '*'
react-native: '*'
typescript: ^5
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
typescript:
optional: true
react-intl@2.9.0:
resolution: {integrity: sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA==}
@@ -5651,6 +5661,10 @@ packages:
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
source-map-support@0.5.13:
resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
@@ -6564,7 +6578,7 @@ snapshots:
'@babel/helper-validator-identifier': 7.25.9
chalk: 2.4.2
js-tokens: 4.0.0
picocolors: 1.1.1
picocolors: 1.0.1
'@babel/parser@7.24.8':
dependencies:
@@ -7266,10 +7280,6 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
'@babel/runtime@7.25.7':
dependencies:
regenerator-runtime: 0.14.1
'@babel/runtime@7.26.7':
dependencies:
regenerator-runtime: 0.14.1
@@ -7929,7 +7939,7 @@ snapshots:
'@material-ui/styles@4.11.5(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.24.8
'@emotion/hash': 0.8.0
'@material-ui/types': 5.1.0(@types/react@17.0.80)
'@material-ui/utils': 4.11.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
@@ -7952,7 +7962,7 @@ snapshots:
'@material-ui/system@4.12.2(@types/react@17.0.80)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.24.8
'@material-ui/utils': 4.11.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2)
csstype: 2.6.21
prop-types: 15.8.1
@@ -7967,7 +7977,7 @@ snapshots:
'@material-ui/utils@4.11.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2)':
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.24.8
prop-types: 15.8.1
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
@@ -8075,7 +8085,7 @@ snapshots:
'@testing-library/dom@7.31.2':
dependencies:
'@babel/code-frame': 7.26.2
'@babel/runtime': 7.26.9
'@babel/runtime': 7.24.8
'@types/aria-query': 4.2.2
aria-query: 4.2.2
chalk: 4.1.2
@@ -8503,7 +8513,7 @@ snapshots:
aria-query@4.2.2:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
'@babel/runtime-corejs3': 7.24.8
aria-query@5.3.0:
@@ -9162,18 +9172,17 @@ snapshots:
postcss-selector-parser: 7.1.0
postcss-value-parser: 4.2.0
css-loader@5.2.7(webpack@5.99.6):
css-loader@7.1.2(webpack@5.99.6):
dependencies:
icss-utils: 5.1.0(postcss@8.4.39)
loader-utils: 2.0.4
postcss: 8.4.39
postcss-modules-extract-imports: 3.1.0(postcss@8.4.39)
postcss-modules-local-by-default: 4.0.5(postcss@8.4.39)
postcss-modules-scope: 3.2.0(postcss@8.4.39)
postcss-modules-values: 4.0.0(postcss@8.4.39)
icss-utils: 5.1.0(postcss@8.5.3)
postcss: 8.5.3
postcss-modules-extract-imports: 3.1.0(postcss@8.5.3)
postcss-modules-local-by-default: 4.2.0(postcss@8.5.3)
postcss-modules-scope: 3.2.1(postcss@8.5.3)
postcss-modules-values: 4.0.0(postcss@8.5.3)
postcss-value-parser: 4.2.0
schema-utils: 3.3.0
semver: 7.6.3
semver: 7.7.1
optionalDependencies:
webpack: 5.99.6
css-minimizer-webpack-plugin@7.0.0(webpack@5.99.6):
@@ -9209,16 +9218,16 @@ snapshots:
css-tree@2.2.1:
dependencies:
mdn-data: 2.0.28
source-map-js: 1.2.0
source-map-js: 1.2.1
css-tree@2.3.1:
dependencies:
mdn-data: 2.0.30
source-map-js: 1.2.0
source-map-js: 1.2.1
css-vendor@2.0.8:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
is-in-browser: 1.1.3
css-what@6.1.0: {}
@@ -9401,7 +9410,7 @@ snapshots:
dom-helpers@5.2.1:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
csstype: 3.1.3
dom-serializer@1.4.1:
@@ -10383,7 +10392,7 @@ snapshots:
history@4.10.1:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
loose-envify: 1.4.0
resolve-pathname: 3.0.0
tiny-invariant: 1.3.3
@@ -10498,9 +10507,9 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
icss-utils@5.1.0(postcss@8.4.39):
icss-utils@5.1.0(postcss@8.5.3):
dependencies:
postcss: 8.4.39
postcss: 8.5.3
idb@7.1.1: {}
@@ -11203,46 +11212,46 @@ snapshots:
jss-plugin-camel-case@10.10.0:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
hyphenate-style-name: 1.1.0
jss: 10.10.0
jss-plugin-default-unit@10.10.0:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
jss: 10.10.0
jss-plugin-global@10.10.0:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
jss: 10.10.0
jss-plugin-nested@10.10.0:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
jss: 10.10.0
tiny-warning: 1.0.3
jss-plugin-props-sort@10.10.0:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
jss: 10.10.0
jss-plugin-rule-value-function@10.10.0:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
jss: 10.10.0
tiny-warning: 1.0.3
jss-plugin-vendor-prefixer@10.10.0:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
css-vendor: 2.0.8
jss: 10.10.0
jss@10.10.0:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
csstype: 3.1.3
is-in-browser: 1.1.3
tiny-warning: 1.0.3
@@ -11431,7 +11440,7 @@ snapshots:
mini-create-react-context@0.4.1(prop-types@15.8.1)(react@17.0.2):
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
prop-types: 15.8.1
react: 17.0.2
tiny-warning: 1.0.3
@@ -11472,6 +11481,8 @@ snapshots:
ms@2.1.3: {}
nanoid@3.3.11: {}
nanoid@3.3.7: {}
natural-compare@1.4.0: {}
@@ -11949,26 +11960,26 @@ snapshots:
postcss: 8.4.39
postcss-selector-parser: 6.1.2
postcss-modules-extract-imports@3.1.0(postcss@8.4.39):
postcss-modules-extract-imports@3.1.0(postcss@8.5.3):
dependencies:
postcss: 8.4.39
postcss: 8.5.3
postcss-modules-local-by-default@4.0.5(postcss@8.4.39):
postcss-modules-local-by-default@4.2.0(postcss@8.5.3):
dependencies:
icss-utils: 5.1.0(postcss@8.4.39)
postcss: 8.4.39
postcss-selector-parser: 6.1.1
icss-utils: 5.1.0(postcss@8.5.3)
postcss: 8.5.3
postcss-selector-parser: 7.1.0
postcss-value-parser: 4.2.0
postcss-modules-scope@3.2.0(postcss@8.4.39):
postcss-modules-scope@3.2.1(postcss@8.5.3):
dependencies:
postcss: 8.4.39
postcss-selector-parser: 6.1.1
postcss: 8.5.3
postcss-selector-parser: 7.1.0
postcss-modules-values@4.0.0(postcss@8.4.39):
postcss-modules-values@4.0.0(postcss@8.5.3):
dependencies:
icss-utils: 5.1.0(postcss@8.4.39)
postcss: 8.4.39
icss-utils: 5.1.0(postcss@8.5.3)
postcss: 8.5.3
postcss-nesting@13.0.1(postcss@8.4.39):
dependencies:
@@ -12150,11 +12161,6 @@ snapshots:
postcss: 8.4.39
postcss-selector-parser: 7.1.0
postcss-selector-parser@6.1.1:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
postcss-selector-parser@6.1.2:
dependencies:
cssesc: 3.0.0
@@ -12184,6 +12190,12 @@ snapshots:
picocolors: 1.0.1
source-map-js: 1.2.0
postcss@8.5.3:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
prelude-ls@1.2.1: {}
pretty-bytes@5.6.0: {}
@@ -12317,14 +12329,15 @@ snapshots:
react-error-overlay@6.0.11: {}
react-i18next@15.1.1(i18next@23.16.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
react-i18next@15.5.1(i18next@23.16.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.8.3):
dependencies:
'@babel/runtime': 7.25.7
'@babel/runtime': 7.27.0
html-parse-stringify: 3.0.1
i18next: 23.16.8
react: 17.0.2
optionalDependencies:
react-dom: 17.0.2(react@17.0.2)
typescript: 5.8.3
react-intl@2.9.0(prop-types@15.8.1)(react@17.0.2):
dependencies:
@@ -12370,7 +12383,7 @@ snapshots:
react-router@5.2.1(react@17.0.2):
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
history: 4.10.1
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0
@@ -12397,7 +12410,7 @@ snapshots:
react-transition-group@4.4.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2):
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.24.8
dom-helpers: 5.2.1
loose-envify: 1.4.0
prop-types: 15.8.1
@@ -12512,7 +12525,7 @@ snapshots:
regenerator-transform@0.15.2:
dependencies:
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
regex-parser@2.3.0: {}
@@ -12835,6 +12848,8 @@ snapshots:
source-map-js@1.2.0: {}
source-map-js@1.2.1: {}
source-map-support@0.5.13:
dependencies:
buffer-from: 1.1.2
@@ -13592,7 +13607,7 @@ snapshots:
'@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1)
'@babel/core': 7.26.10
'@babel/preset-env': 7.26.9(@babel/core@7.26.10)
'@babel/runtime': 7.26.9
'@babel/runtime': 7.27.0
'@rollup/plugin-babel': 5.3.1(@babel/core@7.26.10)(@types/babel__core@7.20.5)(rollup@2.79.2)
'@rollup/plugin-node-resolve': 15.3.0(rollup@2.79.2)
'@rollup/plugin-replace': 2.4.2(rollup@2.79.2)

View File

@@ -60,6 +60,9 @@ strong {
.oc-logo {
height: 80px;
z-index: 0;
}
.oc-logo-container {
margin-bottom: 40px;
}

View File

@@ -72,11 +72,14 @@ const ResponsiveScreen = (props) => {
href={branding.signinPageLogoURI}
target="_blank"
rel="noopener noreferrer"
className='oc-logo-container'
>
{logo}
</a>
) : (
logo
<div className='oc-logo-container'>
{logo}
</div>
)}
<div className={"oc-card"}>
<div className={"oc-card-body"}>{content}</div>

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
mockname: "{{.InterfaceName}}"
outpkg: "mocks"

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
mockname: "{{.InterfaceName}}"
outpkg: "mocks"

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
dir: "pkg/{{.PackageName}}/mocks"
mockname: "{{.InterfaceName}}"

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1,4 +1,7 @@
issue-845-fix: True
resolve-type-alias: False
with-expecter: true
disable-version-string: True
filename: "{{.InterfaceName | snakecase }}.go"
mockname: "{{.InterfaceName}}"
outpkg: "mocks"

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.53.2. DO NOT EDIT.
// Code generated by mockery. DO NOT EDIT.
package mocks

View File

@@ -1069,7 +1069,7 @@ class GraphHelper {
array $body = [],
array $headers = []
): ResponseInterface {
$url = self::getFullUrl($baseUrl, 'drives/' . $urlArguments);
$url = self::getBetaFullUrl($baseUrl, 'drives/' . $urlArguments);
return HttpRequestHelper::get($url, $xRequestId, $user, $password, $headers, $body);
}

View File

@@ -1985,8 +1985,29 @@ trait Provisioning {
if ($this->isTestingWithLdap()) {
$this->deleteLdapUsersAndGroups();
}
$assertionFailed = false;
$errorMessage = '';
// check that created users have only one personal space
try {
$this->setResponse(
$this->spacesContext->listAllAvailableSpaces("admin", "%24filter=driveType+eq+personal")
);
$this->spacesContext->jsonRespondedShouldContainOnlyOneSpace();
$this->spacesContext->jsonRespondedShouldNotContainSpaceWithoutName();
} catch (\Throwable $e) {
$assertionFailed = true;
$errorMessage = $e->getMessage();
echo "\n[WARNING] Space assertion failed: " . $errorMessage . "\n";
}
$this->cleanupDatabaseUsers();
$this->cleanupDatabaseGroups();
if ($assertionFailed) {
throw new \Exception("Space assertion failed:\n" . $errorMessage);
}
}
/**

View File

@@ -1087,6 +1087,74 @@ class SpacesContext implements Context {
);
}
/**
* @return void
* @throws Exception
*/
public function jsonRespondedShouldContainOnlyOneSpace(): void {
$response = $response ?? $this->featureContext->getResponse();
$decodedResponse = $this->featureContext->getJsonDecodedResponse($response);
$userAdmin = $this->featureContext->getAdminUsername();
$aliases = [];
foreach ($decodedResponse['value'] as $space) {
$alias = $space['driveAlias'];
if (isset($aliases[$alias])) {
GraphHelper::disableSpace(
$this->featureContext->getBaseUrl(),
$userAdmin,
$this->featureContext->getPasswordForUser($userAdmin),
$space["id"],
$this->featureContext->getStepLineRef()
);
GraphHelper::deleteSpace(
$this->featureContext->getBaseUrl(),
$userAdmin,
$this->featureContext->getPasswordForUser($userAdmin),
$space["id"],
$this->featureContext->getStepLineRef()
);
Assert::fail(
"Duplicate space found: '$alias'\nResponse:\n" . json_encode($decodedResponse, JSON_PRETTY_PRINT)
);
}
$aliases[$alias] = true;
}
}
/**
* @return void
* @throws Exception
*/
public function jsonRespondedShouldNotContainSpaceWithoutName(): void {
$response = $response ?? $this->featureContext->getResponse();
$decodedResponse = $this->featureContext->getJsonDecodedResponse($response);
$userAdmin = $this->featureContext->getAdminUsername();
foreach ($decodedResponse['value'] as $space) {
if ($space['name'] === "") {
GraphHelper::disableSpace(
$this->featureContext->getBaseUrl(),
$userAdmin,
$this->featureContext->getPasswordForUser($userAdmin),
$space["id"],
$this->featureContext->getStepLineRef()
);
GraphHelper::deleteSpace(
$this->featureContext->getBaseUrl(),
$userAdmin,
$this->featureContext->getPasswordForUser($userAdmin),
$space["id"],
$this->featureContext->getStepLineRef()
);
Assert::fail(
"Space without name found. \nResponse:\n" . json_encode($decodedResponse, JSON_PRETTY_PRINT)
);
}
}
}
/**
* @Then /^the user "([^"]*)" should (not |)have a space called "([^"]*)"$/
*

View File

@@ -1,13 +0,0 @@
//go:build go1.9
// +build go1.9
// "go1.9", from Go version 1.9 onward
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
package roaring
import "math/bits"
func countLeadingZeros(x uint64) int {
return bits.LeadingZeros64(x)
}

View File

@@ -1,13 +0,0 @@
//go:build go1.9
// +build go1.9
// "go1.9", from Go version 1.9 onward
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
package roaring
import "math/bits"
func countTrailingZeros(x uint64) int {
return bits.TrailingZeros64(x)
}

View File

@@ -10,7 +10,7 @@
This is a go version of the Roaring bitmap data structure.
Roaring bitmaps are used by several major systems such as [Apache Lucene][lucene] and derivative systems such as [Solr][solr] and
[Elasticsearch][elasticsearch], [Apache Druid (Incubating)][druid], [LinkedIn Pinot][pinot], [Netflix Atlas][atlas], [Apache Spark][spark], [OpenSearchServer][opensearchserver], [anacrolix/torrent][anacrolix/torrent], [Whoosh][whoosh], [Pilosa][pilosa], [Microsoft Visual Studio Team Services (VSTS)][vsts], and eBay's [Apache Kylin][kylin]. The YouTube SQL Engine, [Google Procella](https://research.google/pubs/pub48388/), uses Roaring bitmaps for indexing.
[Elasticsearch][elasticsearch], [Apache Druid (Incubating)][druid], [LinkedIn Pinot][pinot], [Netflix Atlas][atlas], [Apache Spark][spark], [OpenSearchServer][opensearchserver], [anacrolix/torrent][anacrolix/torrent], [Whoosh][whoosh], [Redpanda](https://github.com/redpanda-data/redpanda), [Pilosa][pilosa], [Microsoft Visual Studio Team Services (VSTS)][vsts], and eBay's [Apache Kylin][kylin]. The YouTube SQL Engine, [Google Procella](https://research.google/pubs/pub48388/), uses Roaring bitmaps for indexing.
[lucene]: https://lucene.apache.org/
[solr]: https://lucene.apache.org/solr/
@@ -163,7 +163,7 @@ they include
- github.com/philhofer/fwd
- github.com/jtolds/gls
Note that the smat library requires Go 1.6 or better.
Note that the smat library requires Go 1.15 or better.
#### Installation
@@ -188,7 +188,7 @@ package main
import (
"fmt"
"github.com/RoaringBitmap/roaring"
"github.com/RoaringBitmap/roaring/v2"
"bytes"
)
@@ -249,15 +249,20 @@ consider the following sample of code:
buf := new(bytes.Buffer)
size,err:=rb.WriteTo(buf)
if err != nil {
t.Errorf("Failed writing")
fmt.Println("Failed writing") // return or panic
}
newrb:= New()
size,err=newrb.ReadFrom(buf)
if err != nil {
t.Errorf("Failed reading")
fmt.Println("Failed reading") // return or panic
}
// if buf is an untrusted source, you should validate the result
// (this adds a bit of complexity but it is necessary for security)
if newrb.Validate() != nil {
fmt.Println("Failed validation") // return or panic
}
if ! rb.Equals(newrb) {
t.Errorf("Cannot retrieve serialized version")
fmt.Println("Cannot retrieve serialized version")
}
```
@@ -280,7 +285,7 @@ package main
import (
"fmt"
"github.com/RoaringBitmap/roaring/roaring64"
"github.com/RoaringBitmap/roaring/v2/roaring64"
"bytes"
)
@@ -356,7 +361,7 @@ https://coveralls.io/github/RoaringBitmap/roaring?branch=master
Type
go test -bench Benchmark -run -
To run benchmarks on [Real Roaring Datasets](https://github.com/RoaringBitmap/real-roaring-datasets)
run the following:
@@ -369,9 +374,8 @@ BENCH_REAL_DATA=1 go test -bench BenchmarkRealData -run -
You can use roaring with gore:
- go get -u github.com/motemen/gore
- go install github.com/x-motemen/gore/cmd/gore@latest
- Make sure that ``$GOPATH/bin`` is in your ``$PATH``.
- go get github.com/RoaringBitmap/roaring
```go
$ gore

View File

@@ -1,6 +1,7 @@
package roaring
import (
"errors"
"fmt"
)
@@ -8,6 +9,11 @@ type arrayContainer struct {
content []uint16
}
var (
ErrArrayIncorrectSort = errors.New("incorrectly sorted array")
ErrArrayInvalidSize = errors.New("invalid array size")
)
func (ac *arrayContainer) String() string {
s := "{"
for it := ac.getShortIterator(); it.hasNext(); {
@@ -26,8 +32,7 @@ func (ac *arrayContainer) fillLeastSignificant16bits(x []uint32, i int, mask uin
_ = x[len(ac.content)-1+i]
_ = ac.content[len(ac.content)-1]
for k := 0; k < len(ac.content); k++ {
x[k+i] =
uint32(ac.content[k]) | mask
x[k+i] = uint32(ac.content[k]) | mask
}
return i + len(ac.content)
}
@@ -60,10 +65,26 @@ func (ac *arrayContainer) minimum() uint16 {
return ac.content[0] // assume not empty
}
func (ac *arrayContainer) safeMinimum() (uint16, error) {
if len(ac.content) == 0 {
return 0, errors.New("empty array")
}
return ac.minimum(), nil
}
func (ac *arrayContainer) maximum() uint16 {
return ac.content[len(ac.content)-1] // assume not empty
}
func (ac *arrayContainer) safeMaximum() (uint16, error) {
if len(ac.content) == 0 {
return 0, errors.New("empty array")
}
return ac.maximum(), nil
}
func (ac *arrayContainer) getSizeInBytes() int {
return ac.getCardinality() * 2
}
@@ -168,7 +189,7 @@ func (ac *arrayContainer) notClose(firstOfRange, lastOfRange int) container {
return ac.toBitmapContainer().not(firstOfRange, lastOfRange+1)
}
answer := newArrayContainer()
answer.content = make([]uint16, newCardinality, newCardinality) //a hack for sure
answer.content = make([]uint16, newCardinality, newCardinality) // a hack for sure
copy(answer.content, ac.content[:startIndex])
outPos := startIndex
@@ -194,11 +215,9 @@ func (ac *arrayContainer) notClose(firstOfRange, lastOfRange int) container {
}
answer.content = answer.content[:newCardinality]
return answer
}
func (ac *arrayContainer) equals(o container) bool {
srb, ok := o.(*arrayContainer)
if ok {
// Check if the containers are the same object.
@@ -239,8 +258,8 @@ func (ac *arrayContainer) toBitmapContainer() *bitmapContainer {
bc := newBitmapContainer()
bc.loadData(ac)
return bc
}
func (ac *arrayContainer) iadd(x uint16) (wasNew bool) {
// Special case adding to the end of the container.
l := len(ac.content)
@@ -352,7 +371,6 @@ func (ac *arrayContainer) ior(a container) container {
return ac.iorArray(x)
case *bitmapContainer:
return a.(*bitmapContainer).orArray(ac)
//return ac.iorBitmap(x) // note: this does not make sense
case *runContainer16:
if x.isFull() {
return x.clone()
@@ -589,7 +607,6 @@ func (ac *arrayContainer) iandBitmap(bc *bitmapContainer) container {
}
ac.content = ac.content[:pos]
return ac
}
func (ac *arrayContainer) xor(a container) container {
@@ -630,7 +647,6 @@ func (ac *arrayContainer) xorArray(value2 *arrayContainer) container {
length := exclusiveUnion2by2(value1.content, value2.content, answer.content)
answer.content = answer.content[:length]
return answer
}
func (ac *arrayContainer) andNot(a container) container {
@@ -822,7 +838,6 @@ func (ac *arrayContainer) inotClose(firstOfRange, lastOfRange int) container {
} else { // no expansion needed
ac.negateRange(buffer, startIndex, lastIndex, firstOfRange, lastOfRange+1)
if cardinalityChange < 0 {
for i := startIndex + newValuesInRange; i < newCardinality; i++ {
ac.content[i] = ac.content[i-cardinalityChange]
}
@@ -915,7 +930,6 @@ func (ac *arrayContainer) rank(x uint16) int {
return answer + 1
}
return -answer - 1
}
func (ac *arrayContainer) selectInt(x uint16) int {
@@ -971,6 +985,179 @@ func (ac *arrayContainer) realloc(size int) {
}
}
// previousValue returns either the target if found or the previous smaller present value.
// If the target is out of bounds a -1 is returned.
// Ex: target=4 ac=[2,3,4,6,7] returns 4
// Ex: target=5 ac=[2,3,4,6,7] returns 4
// Ex: target=6 ac=[2,3,4,6,7] returns 6
// Ex: target=8 ac=[2,3,4,6,7] returns 7
// Ex: target=1 ac=[2,3,4,6,7] returns -1
// Ex: target=0 ac=[2,3,4,6,7] returns -1
func (ac *arrayContainer) previousValue(target uint16) int {
result := binarySearchUntil(ac.content, target)
if result.index == len(ac.content) {
return int(ac.maximum())
}
if result.outOfBounds() {
return -1
}
return int(result.value)
}
// previousAbsentValue returns either the target if not found or the next larger missing value.
// If the target is out of bounds a -1 is returned
// Ex: target=4 ac=[1,2,3,4,6,7] returns 0
// Ex: target=5 ac=[1,2,3,4,6,7] returns 5
// Ex: target=6 ac=[1,2,3,4,6,7] returns 5
// Ex: target=8 ac=[1,2,3,4,6,7] returns 8
func (ac *arrayContainer) previousAbsentValue(target uint16) int {
cardinality := len(ac.content)
if cardinality == 0 {
return int(target)
}
if target > ac.maximum() {
return int(target)
}
result := binarySearchPast(ac.content, target)
if result.notFound() {
return int(target)
}
// If the target was found at index 1, then the next value down must be result.value-1
if result.index == 1 {
if ac.minimum() != result.value-1 {
return int(result.value - 1)
}
}
low := -1
high := result.index
// This uses the pigeon-hole principle.
// the if statement compares the difference in indices vs
// the difference in values. Suppose mid = 10 and result.index = 5
// with ac.content[mid] = 100 and target = 10
// then we have 5 slots for values but we need to fit in 90 values
// so some of the values must be missing
for low+1 < high {
midIndex := (high + low) >> 1
indexDifference := result.index - midIndex
valueDifference := target - ac.content[midIndex]
if indexDifference < int(valueDifference) {
low = midIndex
} else {
high = midIndex
}
}
if high == 0 {
return int(ac.minimum()) - 1
}
return int(ac.content[high] - 1)
}
// nextAbsentValue returns either the target if not found or the next larger missing value.
// If the target is out of bounds a -1 is returned
// Ex: target=4 ac=[1,2,3,4,6,7] returns 5
// Ex: target=5 ac=[1,2,3,4,6,7] returns 5
// Ex: target=0 ac=[1,2,3,4,6,7] returns 0
// Ex: target=8 ac=[1,2,3,4,6,7] returns 8
func (ac *arrayContainer) nextAbsentValue(target uint16) int {
cardinality := len(ac.content)
if cardinality == 0 {
return int(target)
}
if target < ac.minimum() {
return int(target)
}
result := binarySearchPast(ac.content, target)
if result.notFound() {
return int(target)
}
if result.index == cardinality-2 {
if ac.maximum() != result.value+1 {
return int(result.value + 1)
}
}
low := result.index
high := len(ac.content)
// This uses the pigeon-hole principle.
// the if statement compares the difference in indices vs
// the difference in values. Suppose mid = 10 and result.index = 5
// with ac.content[mid] = 100 and target = 10
// then we have 5 slots for values but we need to fit in 90 values
// so some of the values must be missing
for low+1 < high {
midIndex := (high + low) >> 1
indexDifference := midIndex - result.index
valueDifference := ac.content[midIndex] - target
if indexDifference < int(valueDifference) {
high = midIndex
} else {
low = midIndex
}
}
if low == cardinality-1 {
return int(ac.content[cardinality-1] + 1)
}
return int(ac.content[low] + 1)
}
// nextValue returns either the target if found or the next larger value.
// if the target is out of bounds a -1 is returned
//
// Ex: target=4 ac=[1,2,3,4,6,7] returns 4
// Ex: target=5 ac=[1,2,3,4,6,7] returns 6
// Ex: target=6 ac=[1,2,3,4,6,7] returns 6
// Ex: target=0 ac=[1,2,3,4,6,7] returns 1
// Ex: target=100 ac=[1,2,3,4,6,7] returns -1
func (ac *arrayContainer) nextValue(target uint16) int {
cardinality := len(ac.content)
if cardinality == 0 {
return -1
}
//if target < ac.minimum() {
// return -1
//}
//if target > ac.maximum() {
// return -1
// }
result := binarySearchUntil(ac.content, target)
if result.exactMatch {
return int(result.value)
}
if !result.exactMatch && result.index == -1 {
return int(ac.content[0])
}
if result.outOfBounds() {
return -1
}
if result.index < len(ac.content)-1 {
return int(ac.content[result.index+1])
}
return -1
}
func newArrayContainer() *arrayContainer {
p := new(arrayContainer)
return p
@@ -1039,15 +1226,12 @@ func (ac *arrayContainer) numberOfRuns() (nr int) {
// convert to run or array *if needed*
func (ac *arrayContainer) toEfficientContainer() container {
numRuns := ac.numberOfRuns()
sizeAsRunContainer := runContainer16SerializedSizeInBytes(numRuns)
sizeAsBitmapContainer := bitmapContainerSizeInBytes()
card := ac.getCardinality()
sizeAsArrayContainer := arrayContainerSizeInBytes(card)
if sizeAsRunContainer <= minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
if sizeAsRunContainer < minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
return newRunContainer16FromArray(ac)
}
if card <= arrayDefaultMaxSize {
@@ -1099,3 +1283,28 @@ func (ac *arrayContainer) addOffset(x uint16) (container, container) {
return low, high
}
// validate checks cardinality and sort order of the array container
func (ac *arrayContainer) validate() error {
cardinality := ac.getCardinality()
if cardinality <= 0 {
return ErrArrayInvalidSize
}
if cardinality > arrayDefaultMaxSize {
return ErrArrayInvalidSize
}
previous := ac.content[0]
for i := 1; i < len(ac.content); i++ {
next := ac.content[i]
if previous > next {
return ErrArrayIncorrectSort
}
previous = next
}
return nil
}

View File

@@ -1,7 +1,9 @@
package roaring
import (
"errors"
"fmt"
"math/bits"
"unsafe"
)
@@ -56,6 +58,17 @@ func (bc *bitmapContainer) minimum() uint16 {
return MaxUint16
}
func (bc *bitmapContainer) safeMinimum() (uint16, error) {
if len(bc.bitmap) == 0 {
return 0, errors.New("Empty bitmap")
}
val := bc.minimum()
if val == MaxUint16 {
return 0, errors.New("Empty bitmap")
}
return val, nil
}
// i should be non-zero
func clz(i uint64) int {
n := 1
@@ -94,6 +107,17 @@ func (bc *bitmapContainer) maximum() uint16 {
return uint16(0)
}
func (bc *bitmapContainer) safeMaximum() (uint16, error) {
if len(bc.bitmap) == 0 {
return 0, errors.New("Empty bitmap")
}
val := bc.maximum()
if val == uint16(0) {
return 0, errors.New("Empty bitmap")
}
return val, nil
}
func (bc *bitmapContainer) iterate(cb func(x uint16) bool) bool {
iterator := bitmapContainerShortIterator{bc, bc.NextSetBit(0)}
@@ -116,6 +140,7 @@ func (bcsi *bitmapContainerShortIterator) next() uint16 {
bcsi.i = bcsi.ptr.NextSetBit(uint(bcsi.i) + 1)
return uint16(j)
}
func (bcsi *bitmapContainerShortIterator) hasNext() bool {
return bcsi.i >= 0
}
@@ -201,6 +226,7 @@ func (bcmi *bitmapContainerManyIterator) nextMany(hs uint32, buf []uint32) int {
return n
}
// nextMany64 returns the number of values added to the buffer
func (bcmi *bitmapContainerManyIterator) nextMany64(hs uint64, buf []uint64) int {
n := 0
base := bcmi.base
@@ -237,11 +263,10 @@ func (bc *bitmapContainer) getManyIterator() manyIterable {
}
func (bc *bitmapContainer) getSizeInBytes() int {
return len(bc.bitmap) * 8 // + bcBaseBytes
return len(bc.bitmap) * 8
}
func (bc *bitmapContainer) serializedSizeInBytes() int {
//return bc.Msgsize()// NOO! This breaks GetSerializedSizeInBytes
return len(bc.bitmap) * 8
}
@@ -313,6 +338,7 @@ func (bc *bitmapContainer) iaddReturnMinimized(i uint16) container {
return bc
}
// iadd adds the arg i, returning true if not already present
func (bc *bitmapContainer) iadd(i uint16) bool {
x := int(i)
previous := bc.bitmap[x/64]
@@ -441,7 +467,7 @@ func (bc *bitmapContainer) ior(a container) container {
if bc.isFull() {
return newRunContainer16Range(0, MaxUint16)
}
//bc.computeCardinality()
// bc.computeCardinality()
return bc
}
panic(fmt.Errorf("unsupported container type %T", a))
@@ -516,7 +542,7 @@ func (bc *bitmapContainer) orArray(value2 *arrayContainer) container {
}
func (bc *bitmapContainer) orArrayCardinality(value2 *arrayContainer) int {
answer := 0
answer := bc.getCardinality()
c := value2.getCardinality()
for k := 0; k < c; k++ {
// branchless:
@@ -819,9 +845,8 @@ func (bc *bitmapContainer) andBitmap(value2 *bitmapContainer) container {
}
ac := newArrayContainerSize(newcardinality)
fillArrayAND(ac.content, bc.bitmap, value2.bitmap)
ac.content = ac.content[:newcardinality] //not sure why i need this
ac.content = ac.content[:newcardinality]
return ac
}
func (bc *bitmapContainer) intersectsArray(value2 *arrayContainer) bool {
@@ -842,7 +867,6 @@ func (bc *bitmapContainer) intersectsBitmap(value2 *bitmapContainer) bool {
}
}
return false
}
func (bc *bitmapContainer) iandBitmap(value2 *bitmapContainer) container {
@@ -995,7 +1019,7 @@ func (bc *bitmapContainer) iandNotBitmapSurely(value2 *bitmapContainer) containe
return bc
}
func (bc *bitmapContainer) contains(i uint16) bool { //testbit
func (bc *bitmapContainer) contains(i uint16) bool { // testbit
x := uint(i)
w := bc.bitmap[x>>6]
mask := uint64(1) << (x & 63)
@@ -1051,7 +1075,7 @@ func (bc *bitmapContainer) toArrayContainer() *arrayContainer {
}
func (bc *bitmapContainer) fillArray(container []uint16) {
//TODO: rewrite in assembly
// TODO: rewrite in assembly
pos := 0
base := 0
for k := 0; k < len(bc.bitmap); k++ {
@@ -1066,6 +1090,7 @@ func (bc *bitmapContainer) fillArray(container []uint16) {
}
}
// NextSetBit returns the next set bit e.g the next int packed into the bitmaparray
func (bc *bitmapContainer) NextSetBit(i uint) int {
var (
x = i / 64
@@ -1088,12 +1113,22 @@ func (bc *bitmapContainer) NextSetBit(i uint) int {
return -1
}
// PrevSetBit returns the previous set bit e.g the previous int packed into the bitmaparray
func (bc *bitmapContainer) PrevSetBit(i int) int {
if i < 0 {
return -1
}
x := i / 64
if x >= len(bc.bitmap) {
return bc.uPrevSetBit(uint(i))
}
func (bc *bitmapContainer) uPrevSetBit(i uint) int {
var (
x = i >> 6
length = uint(len(bc.bitmap))
)
if x >= length {
return -1
}
@@ -1103,12 +1138,16 @@ func (bc *bitmapContainer) PrevSetBit(i int) int {
w = w << uint(63-b)
if w != 0 {
return i - countLeadingZeros(w)
return int(i) - countLeadingZeros(w)
}
orig := x
x--
for ; x >= 0; x-- {
if x > orig {
return -1
}
for ; x < orig; x-- {
if bc.bitmap[x] != 0 {
return (x * 64) + 63 - countLeadingZeros(bc.bitmap[x])
return int((x*64)+63) - countLeadingZeros(bc.bitmap[x])
}
}
return -1
@@ -1141,7 +1180,6 @@ func (bc *bitmapContainer) numberOfRuns() int {
// convert to run or array *if needed*
func (bc *bitmapContainer) toEfficientContainer() container {
numRuns := bc.numberOfRuns()
sizeAsRunContainer := runContainer16SerializedSizeInBytes(numRuns)
@@ -1149,7 +1187,7 @@ func (bc *bitmapContainer) toEfficientContainer() container {
card := bc.getCardinality()
sizeAsArrayContainer := arrayContainerSizeInBytes(card)
if sizeAsRunContainer <= minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
if sizeAsRunContainer < minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
return newRunContainer16FromBitmapContainer(bc)
}
if card <= arrayDefaultMaxSize {
@@ -1159,7 +1197,6 @@ func (bc *bitmapContainer) toEfficientContainer() container {
}
func newBitmapContainerFromRun(rc *runContainer16) *bitmapContainer {
if len(rc.iv) == 1 {
return newBitmapContainerwithRange(int(rc.iv[0].start), int(rc.iv[0].last()))
}
@@ -1169,7 +1206,7 @@ func newBitmapContainerFromRun(rc *runContainer16) *bitmapContainer {
setBitmapRange(bc.bitmap, int(rc.iv[i].start), int(rc.iv[i].last())+1)
bc.cardinality += int(rc.iv[i].last()) + 1 - int(rc.iv[i].start)
}
//bc.computeCardinality()
// bc.computeCardinality()
return bc
}
@@ -1234,3 +1271,171 @@ func (bc *bitmapContainer) addOffset(x uint16) (container, container) {
return low, high
}
// nextValue returns either the `target` if found or the next largest value.
// if the target is out of bounds a -1 is returned
//
// Example :
// Suppose the bitmap container represents the following slice
// [1,2,10,11,100]
// target=0 returns 1
// target=1 returns 1
// target=10 returns 10
// target=90 returns 100
func (bc *bitmapContainer) nextValue(target uint16) int {
if bc.cardinality == 0 {
return -1
}
return bc.NextSetBit(uint(target))
}
// nextAbsentValue returns the next absent value.
// if the target is out of bounds a -1 is returned
func (bc *bitmapContainer) nextAbsentValue(target uint16) int {
if bc.cardinality == 0 {
return -1
}
var (
x = target >> 6
length = uint(len(bc.bitmap))
)
if uint(x) >= length {
return -1
}
w := bc.bitmap[x]
w = w >> uint(target%64)
if w == 0 {
return int(target)
}
// Check if all 1's
// if statement - we skip the if we have all ones [1,1,1,1...1]
if ^w != 0 {
if countTrailingZeros(w) > 0 {
// we have something like [X,Y,Z, 0,0,0]. This means the target bit is zero
return int(target)
}
// other wise something like [X,Y,0,1,1,1..1], where x and y can be either 1 or 0.
trailing := countTrailingOnes(w)
return int(target) + trailing
}
x++
for ; uint(x) < length; x++ {
if bc.bitmap[x] == 0 {
return int(x * 64)
}
if ^bc.bitmap[x] != 0 {
trailing := countTrailingOnes(bc.bitmap[x])
return int(x*64) + trailing
}
}
return -1
}
// previousValue returns either the `target` if found or the previous largest value.
// if the target is out of bounds a -1 is returned
// Example :
// Suppose the bitmap container represents the following slice
// [1,2,10,11,100]
// target=0 returns -1
// target=1 returns -1
// target=2 returns -1
// target=10 returns 9
// target=50 returns 10
// target=100 returns 99
func (bc *bitmapContainer) previousValue(target uint16) int {
if bc.cardinality == 0 {
return -1
}
return bc.uPrevSetBit(uint(target))
}
// previousAbsentValue returns the next absent value.
func (bc *bitmapContainer) previousAbsentValue(target uint16) int {
if bc.cardinality == 0 {
return -1
}
var (
x = target >> 6
length = uint(len(bc.bitmap))
)
if uint(x) >= length {
return -1
}
w := bc.bitmap[x]
shifted := w >> uint(target%64)
if shifted == 0 {
return int(target)
}
// Check if all 1's
// if statement - we skip if we have all ones [1,1,1,1...1] as no value is absent
if ^shifted != 0 {
if countTrailingZeros(shifted) > 0 {
// we have something like shifted=[X,Y,Z,..., 0,0,0]. This means the target bit is zero
return int(target)
}
// The rotate will rotate the target bit into the leading position.
// We know the target bit is not zero because of the countTrailingZero check above
// We then shift the target bit out of the way.
// Assume a structure like an original structure like [X,Y,Z,..., Target, A, B,C...]
// shifted will be [X,Y,Z...Target]
// shiftedRotated will be [A,B,C....]
// If countLeadingZeros > 0 then A is zero, if not at least A is 1 return
// Else count the number of ones's until a 0
shiftedRotated := bits.RotateLeft64(w, int(64-uint(target%64))-1) << 1
leadingZeros := countLeadingZeros(shiftedRotated)
if leadingZeros > 0 {
return int(target) - 1
}
leadingOnes := countLeadingOnes(shiftedRotated)
if leadingOnes > 0 {
return int(target) - leadingOnes - 1
}
}
x++
for ; uint(x) < length; x++ {
if bc.bitmap[x] == 0 {
return int(x * 64)
}
if ^bc.bitmap[x] != 0 {
trailing := countTrailingOnes(bc.bitmap[x])
return int(x*64) + trailing
}
}
return -1
}
// validate checks that the container size is non-negative
func (bc *bitmapContainer) validate() error {
if bc.cardinality < arrayDefaultMaxSize {
return fmt.Errorf("bitmap container size was less than: %d", arrayDefaultMaxSize)
}
if maxCapacity < len(bc.bitmap)*64 {
return fmt.Errorf("bitmap slize size %d exceeded max capacity %d", maxCapacity, len(bc.bitmap)*64)
}
if bc.cardinality > maxCapacity {
return fmt.Errorf("bitmap container size was greater than: %d", maxCapacity)
}
if bc.cardinality != int(popcntSlice(bc.bitmap)) {
return fmt.Errorf("bitmap container size %d did not match underlying slice length: %d", bc.cardinality, int(popcntSlice(bc.bitmap)))
}
return nil
}

19
vendor/github.com/RoaringBitmap/roaring/v2/clz.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
//go:build go1.9
// +build go1.9
// "go1.9", from Go version 1.9 onward
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
package roaring
import "math/bits"
// countLeadingOnes returns the number of leading zeros bits in x; the result is 64 for x == 0.
func countLeadingZeros(x uint64) int {
return bits.LeadingZeros64(x)
}
// countLeadingOnes returns the number of leading ones bits in x; the result is 0 for x == 0.
func countLeadingOnes(x uint64) int {
return bits.LeadingZeros64(^x)
}

21
vendor/github.com/RoaringBitmap/roaring/v2/ctz.go generated vendored Normal file
View File

@@ -0,0 +1,21 @@
//go:build go1.9
// +build go1.9
// "go1.9", from Go version 1.9 onward
// See https://golang.org/pkg/go/build/#hdr-Build_Constraints
package roaring
import "math/bits"
// countTrailingZeros returns the number of trailing zero bits in x; the result is 64 for x == 0.
func countTrailingZeros(x uint64) int {
return bits.TrailingZeros64(x)
}
// countTrailingOnes returns the number of trailing one bits in x
// The result is 64 for x == 9,223,372,036,854,775,807.
// The result is 0 for x == 0.
func countTrailingOnes(x uint64) int {
return bits.TrailingZeros64(^x)
}

View File

@@ -12,7 +12,7 @@ import (
"io"
"strconv"
"github.com/RoaringBitmap/roaring/internal"
"github.com/RoaringBitmap/roaring/v2/internal"
"github.com/bits-and-blooms/bitset"
)
@@ -26,7 +26,6 @@ func (rb *Bitmap) ToBase64() (string, error) {
buf := new(bytes.Buffer)
_, err := rb.WriteTo(buf)
return base64.StdEncoding.EncodeToString(buf.Bytes()), err
}
// FromBase64 deserializes a bitmap from Base64
@@ -54,10 +53,12 @@ func (rb *Bitmap) ToBytes() ([]byte, error) {
return rb.highlowcontainer.toBytes()
}
const wordSize = uint64(64)
const log2WordSize = uint64(6)
const capacity = ^uint64(0)
const bitmapContainerSize = (1 << 16) / 64 // bitmap size in words
const (
wordSize = uint64(64)
log2WordSize = uint64(6)
capacity = ^uint64(0)
bitmapContainerSize = (1 << 16) / 64 // bitmap size in words
)
// DenseSize returns the size of the bitmap when stored as a dense bitmap.
func (rb *Bitmap) DenseSize() uint64 {
@@ -276,14 +277,19 @@ func (rb *Bitmap) Checksum() uint64 {
return hash
}
// FromUnsafeBytes reads a serialized version of this bitmap from the byte buffer without copy.
// FromUnsafeBytes reads a serialized version of this bitmap from the byte buffer without copy
// (for advanced users only, you must be an expert Go programmer!).
// E.g., you can use this method to read a serialized bitmap from a memory-mapped file written out
// with the WriteTo method.
// The format specification is
// https://github.com/RoaringBitmap/RoaringFormatSpec
// It is the caller's responsibility to ensure that the input data is not modified and remains valid for the entire lifetime of this bitmap.
// This method avoids small allocations but holds references to the input data buffer. It is GC-friendly, but it may consume more memory eventually.
// The containers in the resulting bitmap are immutable containers tied to the provided byte array and they rely on
// copy-on-write which means that modifying them creates copies. Thus FromUnsafeBytes is more likely to be appropriate for read-only use cases,
// when the resulting bitmap can be considered immutable.
//
// See also the FromBuffer function.
// See also the FromBuffer function. We recommend benchmarking both functions to determine which one is more suitable for your use case.
// See https://github.com/RoaringBitmap/roaring/pull/395 for more details.
func (rb *Bitmap) FromUnsafeBytes(data []byte, cookieHeader ...byte) (p int64, err error) {
stream := internal.NewByteBuffer(data)
@@ -291,11 +297,13 @@ func (rb *Bitmap) FromUnsafeBytes(data []byte, cookieHeader ...byte) (p int64, e
}
// ReadFrom reads a serialized version of this bitmap from stream.
// E.g., you can use this method to read a serialized bitmap from a file written
// with the WriteTo method.
// The format is compatible with other RoaringBitmap
// implementations (Java, C) and is documented here:
// https://github.com/RoaringBitmap/RoaringFormatSpec
// Since io.Reader is regarded as a stream and cannot be read twice.
// So add cookieHeader to accept the 4-byte data that has been read in roaring64.ReadFrom.
// Since io.Reader is regarded as a stream and cannot be read twice,
// we add cookieHeader to accept the 4-byte data that has been read in roaring64.ReadFrom.
// It is not necessary to pass cookieHeader when call roaring.ReadFrom to read the roaring32 data directly.
func (rb *Bitmap) ReadFrom(reader io.Reader, cookieHeader ...byte) (p int64, err error) {
stream, ok := reader.(internal.ByteInput)
@@ -313,7 +321,18 @@ func (rb *Bitmap) ReadFrom(reader io.Reader, cookieHeader ...byte) (p int64, err
return
}
// FromBuffer creates a bitmap from its serialized version stored in buffer
// MustReadFrom calls ReadFrom internally.
// After deserialization Validate will be called.
// If the Bitmap fails to validate, a panic with the validation error will be thrown
func (rb *Bitmap) MustReadFrom(reader io.Reader, cookieHeader ...byte) (p int64, err error) {
rb.ReadFrom(reader, cookieHeader...)
if err := rb.Validate(); err != nil {
panic(err)
}
return
}
// FromBuffer creates a bitmap from its serialized version stored in buffer (E.g., as written by WriteTo).
//
// The format specification is available here:
// https://github.com/RoaringBitmap/RoaringFormatSpec
@@ -960,7 +979,6 @@ func (rb *Bitmap) CheckedAdd(x uint32) bool {
newac := newArrayContainer()
rb.highlowcontainer.insertNewKeyValueAt(-i-1, hb, newac.iaddReturnMinimized(lowbits(x)))
return true
}
// AddInt adds the integer x to the bitmap (convenience method: the parameter is casted to uint32 and we call Add)
@@ -998,7 +1016,6 @@ func (rb *Bitmap) CheckedRemove(x uint32) bool {
return C.getCardinality() < oldcard
}
return false
}
// IsEmpty returns true if the Bitmap is empty (it is faster than doing (GetCardinality() == 0))
@@ -1088,7 +1105,7 @@ main:
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else { //s1 > s2
} else { // s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
@@ -1187,7 +1204,7 @@ main:
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else { //s1 > s2
} else { // s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
@@ -1256,7 +1273,7 @@ main:
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else { //s1 > s2
} else { // s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
@@ -1396,7 +1413,7 @@ main:
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else { //s1 > s2
} else { // s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
@@ -1584,7 +1601,7 @@ main:
}
s1 = x1.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else { //s1 > s2
} else { // s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
@@ -1632,7 +1649,6 @@ func BitmapOf(dat ...uint32) *Bitmap {
// The function uses 64-bit parameters even though a Bitmap stores 32-bit values because it is allowed and meaningful to use [0,uint64(0x100000000)) as a range
// while uint64(0x100000000) cannot be represented as a 32-bit value.
func (rb *Bitmap) Flip(rangeStart, rangeEnd uint64) {
if rangeEnd > MaxUint32+1 {
panic("rangeEnd > MaxUint32+1")
}
@@ -1869,6 +1885,206 @@ func (rb *Bitmap) CloneCopyOnWriteContainers() {
rb.highlowcontainer.cloneCopyOnWriteContainers()
}
// NextValue returns the next largest value in the bitmap, or -1
// if none is present. This function should not be used inside
// a performance-sensitive loop: prefer iterators if
// performance is a concern.
func (rb *Bitmap) NextValue(target uint32) int64 {
originalKey := highbits(target)
query := lowbits(target)
var nextValue int64
nextValue = -1
containerIndex := rb.highlowcontainer.advanceUntil(originalKey, -1)
for containerIndex < rb.highlowcontainer.size() && nextValue == -1 {
containerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
container := rb.highlowcontainer.getContainer(containerKey)
// if containerKey > orginalKey then we are past the container which mapped to the orignal key
// in that case we can just return the minimum from that container
var responseBit int64
if containerKey > originalKey {
bit, err := container.safeMinimum()
if err == nil {
responseBit = -1
}
responseBit = int64(bit)
} else {
responseBit = int64(container.nextValue(query))
}
if responseBit == -1 {
nextValue = -1
} else {
nextValue = int64(combineLoHi32(uint32(responseBit), uint32(containerKey)))
}
containerIndex++
}
return nextValue
}
// PreviousValue returns the previous largest value in the bitmap, or -1
// if none is present. This function should not be used inside
// a performance-sensitive loop: prefer iterators if
// performance is a concern.
func (rb *Bitmap) PreviousValue(target uint32) int64 {
if rb.IsEmpty() {
return -1
}
originalKey := highbits(uint32(target))
query := lowbits(uint32(target))
var prevValue int64
prevValue = -1
containerIndex := rb.highlowcontainer.advanceUntil(originalKey, -1)
if containerIndex == rb.highlowcontainer.size() {
return int64(rb.Maximum())
}
if rb.highlowcontainer.getKeyAtIndex(containerIndex) > originalKey {
// target absent, key of first container after target too high
containerIndex--
}
for containerIndex != -1 && prevValue == -1 {
containerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
container := rb.highlowcontainer.getContainer(containerKey)
// if containerKey > originalKey then we are past the container which mapped to the original key
// in that case we can just return the minimum from that container
var responseBit int
if containerKey < originalKey {
bit, err := container.safeMaximum()
if err == nil {
responseBit = -1
}
responseBit = int(bit)
} else {
responseBit = container.previousValue(query)
}
if responseBit == -1 {
prevValue = -1
} else {
prevValue = int64(combineLoHi32(uint32(responseBit), uint32(containerKey)))
}
containerIndex--
}
return prevValue
}
// NextAbsentValue returns the next largest missing value in the bitmap, or -1
// if none is present. This function should not be used inside
// a performance-sensitive loop: prefer iterators if
// performance is a concern.
func (rb *Bitmap) NextAbsentValue(target uint32) int64 {
originalKey := highbits(target)
query := lowbits(target)
var nextValue int64
nextValue = -1
containerIndex := rb.highlowcontainer.advanceUntil(originalKey, -1)
if containerIndex == rb.highlowcontainer.size() {
// if we are here it means no container found, just return the target
return int64(target)
}
containerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
keyspace := uint32(containerKey) << 16
if target < keyspace {
// target is less than the start of the keyspace start
// that means target cannot be in the keyspace
return int64(target)
}
container := rb.highlowcontainer.getContainer(containerKey)
nextValue = int64(container.nextAbsentValue(query))
for {
if nextValue != (1 << 16) {
return int64(combineLoHi32(uint32(nextValue), keyspace))
}
if containerIndex == rb.highlowcontainer.size()-1 {
val, err := container.safeMaximum()
if err == nil {
return -1
}
return int64(val) + 1
}
containerIndex++
nextContainerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
if containerKey < nextContainerKey {
// There is a gap between keys
// Just increment the current key and shift to get HoB
return int64(containerKey+1) << 16
}
containerKey = nextContainerKey
container = rb.highlowcontainer.getContainer(containerKey)
nextValue = int64(container.nextAbsentValue(0))
}
}
// PreviousAbsentValue returns the previous largest missing value in the bitmap, or -1
// if none is present. This function should not be used inside
// a performance-sensitive loop: prefer iterators if
// performance is a concern.
func (rb *Bitmap) PreviousAbsentValue(target uint32) int64 {
originalKey := highbits(target)
query := lowbits(target)
var prevValue int64
prevValue = -1
containerIndex := rb.highlowcontainer.advanceUntil(originalKey, -1)
if containerIndex == rb.highlowcontainer.size() {
// if we are here it means no container found, just return the target
return int64(target)
}
if containerIndex == -1 {
// if we are here it means no container found, just return the target
return int64(target)
}
containerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
keyspace := uint32(containerKey) << 16
if target < keyspace {
// target is less than the start of the keyspace start
// that means target cannot be in the keyspace
return int64(target)
}
container := rb.highlowcontainer.getContainer(containerKey)
prevValue = int64(container.previousAbsentValue(query))
for {
if prevValue != -1 {
return int64(combineLoHi32(uint32(prevValue), keyspace))
}
if containerIndex == 0 {
val, err := container.safeMinimum()
if err == nil {
// OR panic, Java panics
return -1
}
return int64(val) - 1
}
containerIndex--
nextContainerKey := rb.highlowcontainer.getKeyAtIndex(containerIndex)
if nextContainerKey < containerKey-1 {
// There is a gap between keys, eg missing container
// Just decrement the current key and shift to get HoB of the missing container
return (int64(containerKey) << 16) - 1
}
containerKey = nextContainerKey
container = rb.highlowcontainer.getContainer(containerKey)
highestPossible16 := (1 << 16) - 1
prevValue = int64(container.previousAbsentValue(uint16(highestPossible16)))
}
}
// FlipInt calls Flip after casting the parameters (convenience method)
func FlipInt(bm *Bitmap, rangeStart, rangeEnd int) *Bitmap {
return Flip(bm, uint64(rangeStart), uint64(rangeEnd))
@@ -1916,3 +2132,10 @@ func (rb *Bitmap) Stats() Statistics {
}
return stats
}
// Validate checks if the bitmap is internally consistent.
// You may call it after deserialization to check that the bitmap is valid.
// This function returns an error if the bitmap is invalid, nil otherwise.
func (rb *Bitmap) Validate() error {
return rb.highlowcontainer.validate()
}

View File

@@ -3,17 +3,9 @@ package roaring64
import (
"fmt"
"io"
"math/bits"
"math/big"
"runtime"
"sync"
"sync/atomic"
)
const (
// Min64BitSigned - Minimum 64 bit value
Min64BitSigned = -9223372036854775808
// Max64BitSigned - Maximum 64 bit value
Max64BitSigned = 9223372036854775807
)
// BSI is at its simplest is an array of bitmaps that represent an encoded
@@ -32,13 +24,16 @@ type BSI struct {
runOptimized bool
}
// NewBSI constructs a new BSI. Min/Max values are optional. If set to 0
// then the underlying BSI will be automatically sized.
// NewBSI constructs a new BSI. Note that it is your responsibility to ensure that
// the min/max values are set correctly. Queries CompareValue, MinMax, etc. will not
// work correctly if the min/max values are not set correctly.
func NewBSI(maxValue int64, minValue int64) *BSI {
bitsz := bits.Len64(uint64(minValue))
if bits.Len64(uint64(maxValue)) > bitsz {
bitsz = bits.Len64(uint64(maxValue))
bitszmin := big.NewInt(minValue).BitLen() + 1
bitszmax := big.NewInt(maxValue).BitLen() + 1
bitsz := bitszmin
if bitszmax > bitsz {
bitsz = bitszmax
}
ba := make([]Bitmap, bitsz)
return &BSI{bA: ba, MaxValue: maxValue, MinValue: minValue}
@@ -81,41 +76,97 @@ func (b *BSI) GetCardinality() uint64 {
// BitCount returns the number of bits needed to represent values.
func (b *BSI) BitCount() int {
return len(b.bA)
return len(b.bA) - 1 // Exclude sign bit
}
// SetValue sets a value for a given columnID.
func (b *BSI) SetValue(columnID uint64, value int64) {
// IsBigUInt returns the number of bits needed to represent values.
func (b *BSI) isBig() bool {
return len(b.bA) > 64
}
// IsNegative returns true for negative values
func (b *BSI) IsNegative(columnID uint64) bool {
if len(b.bA) == 0 {
return false
}
return b.bA[b.BitCount()].Contains(columnID)
}
// SetBigValue sets a value that exceeds 64 bits
func (b *BSI) SetBigValue(columnID uint64, value *big.Int) {
// If max/min values are set to zero then automatically determine bit array size
if b.MaxValue == 0 && b.MinValue == 0 {
minBits := bits.Len64(uint64(value))
minBits := value.BitLen() + 1
if minBits == 1 {
minBits = 2
}
for len(b.bA) < minBits {
b.bA = append(b.bA, Bitmap{})
}
}
for i := 0; i < b.BitCount(); i++ {
if uint64(value)&(1<<uint64(i)) > 0 {
b.bA[i].Add(columnID)
} else {
for i := b.BitCount(); i >= 0; i-- {
if value.Bit(i) == 0 {
b.bA[i].Remove(columnID)
} else {
b.bA[i].Add(columnID)
}
}
b.eBM.Add(columnID)
}
// SetValue sets a value for a given columnID.
func (b *BSI) SetValue(columnID uint64, value int64) {
b.SetBigValue(columnID, big.NewInt(value))
}
// GetValue gets the value at the column ID. Second param will be false for non-existent values.
func (b *BSI) GetValue(columnID uint64) (value int64, exists bool) {
bv, exists := b.GetBigValue(columnID)
if !exists {
return
}
if !bv.IsInt64() {
if bv.Sign() == -1 {
msg := fmt.Errorf("can't represent a negative %d bit value as an int64", b.BitCount())
panic(msg)
}
if bv.Sign() == 1 {
msg := fmt.Errorf("can't represent a positive %d bit value as an int64", b.BitCount())
panic(msg)
}
}
return bv.Int64(), exists
}
// GetBigValue gets the value at the column ID. Second param will be false for non-existent values.
func (b *BSI) GetBigValue(columnID uint64) (value *big.Int, exists bool) {
exists = b.eBM.Contains(columnID)
if !exists {
return
}
for i := 0; i < b.BitCount(); i++ {
val := big.NewInt(0)
for i := b.BitCount(); i >= 0; i-- {
if b.bA[i].Contains(columnID) {
value |= 1 << i
bigBit := big.NewInt(1)
bigBit.Lsh(bigBit, uint(i))
val.Or(val, bigBit)
}
}
return
if b.IsNegative(columnID) {
val = negativeTwosComplementToInt(val)
}
return val, exists
}
func negativeTwosComplementToInt(val *big.Int) *big.Int {
inverted := new(big.Int).Not(val)
mask := new(big.Int).Lsh(big.NewInt(1), uint(val.BitLen()))
inverted.And(inverted, mask.Sub(mask, big.NewInt(1)))
inverted.Add(inverted, big.NewInt(1))
val.Neg(inverted)
return val
}
type action func(t *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.WaitGroup)
@@ -235,13 +286,15 @@ const (
type task struct {
bsi *BSI
op Operation
valueOrStart int64
end int64
values map[int64]struct{}
valueOrStart *big.Int
end *big.Int
values map[string]struct{}
bits *Bitmap
}
// CompareValue compares value.
// Values should be in the range of the BSI (max, min). If the value is outside the range, the result
// might erroneous. The operation parameter indicates the type of comparison to be made.
// For all operations with the exception of RANGE, the value to be compared is specified by valueOrStart.
// For the RANGE parameter the comparison criteria is >= valueOrStart and <= end.
// The parallelism parameter indicates the number of CPU threads to be applied for processing. A value
@@ -249,6 +302,26 @@ type task struct {
func (b *BSI) CompareValue(parallelism int, op Operation, valueOrStart, end int64,
foundSet *Bitmap) *Bitmap {
return b.CompareBigValue(parallelism, op, big.NewInt(valueOrStart), big.NewInt(end), foundSet)
}
// CompareBigValue compares value.
// Values should be in the range of the BSI (max, min). If the value is outside the range, the result
// might erroneous. The operation parameter indicates the type of comparison to be made.
// For all operations with the exception of RANGE, the value to be compared is specified by valueOrStart.
// For the RANGE parameter the comparison criteria is >= valueOrStart and <= end.
// The parallelism parameter indicates the number of CPU threads to be applied for processing. A value
// of zero indicates that all available CPU resources will be potentially utilized.
func (b *BSI) CompareBigValue(parallelism int, op Operation, valueOrStart, end *big.Int,
foundSet *Bitmap) *Bitmap {
if valueOrStart == nil {
valueOrStart = b.MinMaxBig(parallelism, MIN, &b.eBM)
}
if end == nil && op == RANGE {
end = b.MinMaxBig(parallelism, MAX, &b.eBM)
}
comp := &task{bsi: b, op: op, valueOrStart: valueOrStart, end: end}
if foundSet == nil {
return parallelExecutor(parallelism, comp, compareValue, &b.eBM)
@@ -256,6 +329,53 @@ func (b *BSI) CompareValue(parallelism int, op Operation, valueOrStart, end int6
return parallelExecutor(parallelism, comp, compareValue, foundSet)
}
// Returns a twos complement value given a value, the return will be bit extended to 'bits' length
// if the value is negative
func twosComplement(num *big.Int, bitCount int) *big.Int {
// Check if the number is negative
isNegative := num.Sign() < 0
// Get the absolute value if negative
abs := new(big.Int).Abs(num)
// Convert to binary string
binStr := abs.Text(2)
// Pad with zeros to the left
if len(binStr) < bitCount {
binStr = fmt.Sprintf("%0*s", bitCount, binStr)
}
// If negative, calculate two's complement
if isNegative {
// Invert bits
inverted := make([]byte, len(binStr))
for i := range binStr {
if binStr[i] == '0' {
inverted[i] = '1'
} else {
inverted[i] = '0'
}
}
// Add 1
carry := byte(1)
for i := len(inverted) - 1; i >= 0; i-- {
inverted[i] += carry
if inverted[i] == '2' {
inverted[i] = '0'
} else {
break
}
}
binStr = string(inverted)
}
bigInt := new(big.Int)
_, _ = bigInt.SetString(binStr, 2)
return bigInt
}
func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.WaitGroup) {
defer wg.Done()
@@ -265,32 +385,31 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
results.RunOptimize()
}
x := e.bsi.BitCount()
startIsNegative := x == 64 && uint64(e.valueOrStart)&(1<<uint64(x-1)) > 0
endIsNegative := x == 64 && uint64(e.end)&(1<<uint64(x-1)) > 0
startIsNegative := e.valueOrStart.Sign() == -1
endIsNegative := true
if e.end != nil {
endIsNegative = e.end.Sign() == -1
}
for i := 0; i < len(batch); i++ {
cID := batch[i]
eq1, eq2 := true, true
lt1, lt2, gt1 := false, false, false
j := e.bsi.BitCount() - 1
isNegative := false
if x == 64 {
isNegative = e.bsi.bA[j].Contains(cID)
j--
}
j := e.bsi.BitCount()
isNegative := e.bsi.IsNegative(cID)
compStartValue := e.valueOrStart
compEndValue := e.end
if isNegative != startIsNegative {
compStartValue = ^e.valueOrStart + 1
compStartValue = twosComplement(e.valueOrStart, e.bsi.BitCount()+1)
}
if isNegative != endIsNegative {
compEndValue = ^e.end + 1
if isNegative != endIsNegative && e.end != nil {
compEndValue = twosComplement(e.end, e.bsi.BitCount()+1)
}
for ; j >= 0; j-- {
sliceContainsBit := e.bsi.bA[j].Contains(cID)
if uint64(compStartValue)&(1<<uint64(j)) > 0 {
if compStartValue.Bit(j) == 1 {
// BIT in value is SET
if !sliceContainsBit {
if eq1 {
@@ -303,7 +422,9 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
}
}
eq1 = false
break
if e.op != RANGE {
break
}
}
}
} else {
@@ -319,6 +440,7 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
}
}
eq1 = false
if e.op != RANGE {
break
}
@@ -326,7 +448,7 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
}
}
if e.op == RANGE && uint64(compEndValue)&(1<<uint64(j)) > 0 {
if e.op == RANGE && compEndValue.Bit(j) == 1 {
// BIT in value is SET
if !sliceContainsBit {
if eq2 {
@@ -347,11 +469,9 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
lt2 = true
}
eq2 = false
break
}
}
}
}
switch e.op {
@@ -387,15 +507,24 @@ func compareValue(e *task, batch []uint64, resultsChan chan *Bitmap, wg *sync.Wa
resultsChan <- results
}
// MinMax - Find minimum or maximum value.
// MinMax - Find minimum or maximum int64 value.
func (b *BSI) MinMax(parallelism int, op Operation, foundSet *Bitmap) int64 {
return b.MinMaxBig(parallelism, op, foundSet).Int64()
}
// MinMaxBig - Find minimum or maximum value.
func (b *BSI) MinMaxBig(parallelism int, op Operation, foundSet *Bitmap) *big.Int {
var n int = parallelism
if n == 0 {
n = runtime.NumCPU()
}
resultsChan := make(chan int64, n)
resultsChan := make(chan *big.Int, n)
if foundSet == nil {
foundSet = &b.eBM
}
card := foundSet.GetCardinality()
x := card / uint64(n)
@@ -418,63 +547,87 @@ func (b *BSI) MinMax(parallelism int, op Operation, foundSet *Bitmap) int64 {
wg.Wait()
close(resultsChan)
var minMax int64
var minMax *big.Int
minSigned, maxSigned := minMaxSignedInt(b.BitCount() + 1)
if op == MAX {
minMax = Min64BitSigned
minMax = minSigned
} else {
minMax = Max64BitSigned
minMax = maxSigned
}
for val := range resultsChan {
if (op == MAX && val > minMax) || (op == MIN && val <= minMax) {
if (op == MAX && val.Cmp(minMax) > 0) || (op == MIN && val.Cmp(minMax) <= 0) {
minMax = val
}
}
return minMax
}
func (b *BSI) minOrMax(op Operation, batch []uint64, resultsChan chan int64, wg *sync.WaitGroup) {
func minMaxSignedInt(bits int) (*big.Int, *big.Int) {
// Calculate the maximum value
max := new(big.Int).Lsh(big.NewInt(1), uint(bits-1))
max.Sub(max, big.NewInt(1))
// Calculate the minimum value
min := new(big.Int).Neg(max)
min.Sub(min, big.NewInt(1))
return min, max
}
func (b *BSI) minOrMax(op Operation, batch []uint64, resultsChan chan *big.Int, wg *sync.WaitGroup) {
defer wg.Done()
x := b.BitCount()
var value int64 = Max64BitSigned
x := b.BitCount() + 1
var value *big.Int
minSigned, maxSigned := minMaxSignedInt(x)
if op == MAX {
value = Min64BitSigned
value = minSigned
} else {
value = maxSigned
}
for i := 0; i < len(batch); i++ {
cID := batch[i]
eq := true
lt, gt := false, false
j := b.BitCount() - 1
var cVal int64
valueIsNegative := uint64(value)&(1<<uint64(x-1)) > 0 && bits.Len64(uint64(value)) == 64
isNegative := false
if x == 64 {
isNegative = b.bA[j].Contains(cID)
if isNegative {
cVal |= 1 << uint64(j)
}
j--
}
j := b.BitCount()
cVal := new(big.Int)
valueIsNegative := value.Sign() == -1
isNegative := b.IsNegative(cID)
compValue := value
if isNegative != valueIsNegative {
compValue = ^value + 1
// convert compValue to twos complement
inverted := new(big.Int).Not(compValue)
mask := new(big.Int).Lsh(big.NewInt(1), uint(compValue.BitLen()))
inverted.And(inverted, mask.Sub(mask, big.NewInt(1)))
inverted.Add(inverted, big.NewInt(1))
}
done := false
for ; j >= 0; j-- {
sliceContainsBit := b.bA[j].Contains(cID)
if sliceContainsBit {
cVal |= 1 << uint64(j)
bigBit := big.NewInt(1)
bigBit.Lsh(bigBit, uint(j))
cVal.Or(cVal, bigBit)
if isNegative {
cVal = negativeTwosComplementToInt(cVal)
}
}
if uint64(compValue)&(1<<uint64(j)) > 0 {
if done {
continue
}
if compValue.Bit(j) == 1 {
// BIT in value is SET
if !sliceContainsBit {
if eq {
eq = false
if op == MAX && valueIsNegative && !isNegative {
gt = true
break
done = true
}
if op == MIN && (!valueIsNegative || (valueIsNegative == isNegative)) {
lt = true
@@ -491,11 +644,13 @@ func (b *BSI) minOrMax(op Operation, batch []uint64, resultsChan chan int64, wg
}
if op == MAX && (valueIsNegative || (valueIsNegative == isNegative)) {
gt = true
done = true
}
}
}
}
}
if lt || gt {
value = cVal
}
@@ -506,19 +661,37 @@ func (b *BSI) minOrMax(op Operation, batch []uint64, resultsChan chan int64, wg
// Sum all values contained within the foundSet. As a convenience, the cardinality of the foundSet
// is also returned (for calculating the average).
func (b *BSI) Sum(foundSet *Bitmap) (sum int64, count uint64) {
func (b *BSI) Sum(foundSet *Bitmap) (int64, uint64) {
val, count := b.SumBigValues(foundSet)
return val.Int64(), count
}
// SumBigValues - Sum all values contained within the foundSet. As a convenience, the cardinality of the foundSet
// is also returned (for calculating the average). This method will sum arbitrarily large values.
func (b *BSI) SumBigValues(foundSet *Bitmap) (sum *big.Int, count uint64) {
if foundSet == nil {
foundSet = &b.eBM
}
sum = new(big.Int)
count = foundSet.GetCardinality()
resultsChan := make(chan int64, b.BitCount())
var wg sync.WaitGroup
for i := 0; i < b.BitCount(); i++ {
wg.Add(1)
go func(j int) {
defer wg.Done()
atomic.AddInt64(&sum, int64(foundSet.AndCardinality(&b.bA[j])<<uint(j)))
resultsChan <- int64(foundSet.AndCardinality(&b.bA[j]) << uint(j))
}(i)
}
wg.Wait()
return
close(resultsChan)
for val := range resultsChan {
sum.Add(sum, big.NewInt(val))
}
sum.Sub(sum, big.NewInt(int64(foundSet.AndCardinality(&b.bA[b.BitCount()])<<uint(b.BitCount()))))
return sum, count
}
// Transpose calls b.IntersectAndTranspose(0, b.eBM)
@@ -533,7 +706,9 @@ func (b *BSI) Transpose() *Bitmap {
//
// TODO: This implementation is functional but not performant, needs to be re-written perhaps using SIMD SSE2 instructions.
func (b *BSI) IntersectAndTranspose(parallelism int, foundSet *Bitmap) *Bitmap {
if foundSet == nil {
foundSet = &b.eBM
}
trans := &task{bsi: b}
return parallelExecutor(parallelism, trans, transpose, foundSet)
}
@@ -563,12 +738,12 @@ func (b *BSI) ParOr(parallelism int, bsis ...*BSI) {
bits := len(b.bA)
for i := 0; i < len(bsis); i++ {
if len(bsis[i].bA) > bits {
bits = bsis[i].BitCount()
bits = len(bsis[i].bA )
}
}
// Make sure we have enough bit slices
for bits > b.BitCount() {
for bits > len(b.bA) {
bm := Bitmap{}
bm.RunOptimize()
b.bA = append(b.bA, bm)
@@ -725,10 +900,20 @@ func (b *BSI) WriteTo(w io.Writer) (n int64, err error) {
// BatchEqual returns a bitmap containing the column IDs where the values are contained within the list of values provided.
func (b *BSI) BatchEqual(parallelism int, values []int64) *Bitmap {
//convert list of int64 values to big.Int(s)
bigValues := make([]*big.Int, len(values))
for i, v := range values {
bigValues[i] = big.NewInt(v)
}
return b.BatchEqualBig(parallelism, bigValues)
}
valMap := make(map[int64]struct{}, len(values))
// BatchEqualBig returns a bitmap containing the column IDs where the values are contained within the list of values provided.
func (b *BSI) BatchEqualBig(parallelism int, values []*big.Int) *Bitmap {
valMap := make(map[string]struct{}, len(values))
for i := 0; i < len(values); i++ {
valMap[values[i]] = struct{}{}
valMap[string(values[i].Bytes())] = struct{}{}
}
comp := &task{bsi: b, values: valMap}
return parallelExecutor(parallelism, comp, batchEqual, &b.eBM)
@@ -746,8 +931,8 @@ func batchEqual(e *task, batch []uint64, resultsChan chan *Bitmap,
for i := 0; i < len(batch); i++ {
cID := batch[i]
if value, ok := e.bsi.GetValue(uint64(cID)); ok {
if _, yes := e.values[int64(value)]; yes {
if value, ok := e.bsi.GetBigValue(uint64(cID)); ok {
if _, yes := e.values[string(value.Bytes())]; yes {
results.Add(cID)
}
}
@@ -786,8 +971,8 @@ func (b *BSI) ClearValues(foundSet *Bitmap) {
// NewBSIRetainSet - Construct a new BSI from a clone of existing BSI, retain only values contained in foundSet
func (b *BSI) NewBSIRetainSet(foundSet *Bitmap) *BSI {
newBSI := NewBSI(b.MaxValue, b.MinValue)
newBSI.bA = make([]Bitmap, b.BitCount())
newBSI := NewDefaultBSI()
newBSI.bA = make([]Bitmap, b.BitCount()+1)
var wg sync.WaitGroup
wg.Add(1)
go func() {
@@ -823,13 +1008,13 @@ func (b *BSI) Add(other *BSI) {
func (b *BSI) addDigit(foundSet *Bitmap, i int) {
if i >= len(b.bA) {
if i >= b.BitCount()+1 || b.BitCount() == 0 {
b.bA = append(b.bA, Bitmap{})
}
carry := And(&b.bA[i], foundSet)
b.bA[i].Xor(foundSet)
if !carry.IsEmpty() {
if i+1 >= len(b.bA) {
if i+1 >= b.BitCount() {
b.bA = append(b.bA, Bitmap{})
}
b.addDigit(carry, i+1)
@@ -841,7 +1026,12 @@ func (b *BSI) addDigit(foundSet *Bitmap, i int) {
// is useful for situations where there is a one-to-many relationship between the vectored integer sets. The resulting BSI
// contains the number of times a particular value appeared in the input BSI.
func (b *BSI) TransposeWithCounts(parallelism int, foundSet, filterSet *Bitmap) *BSI {
if foundSet == nil {
foundSet = &b.eBM
}
if filterSet == nil {
filterSet = &b.eBM
}
return parallelExecutorBSIResults(parallelism, b, transposeWithCounts, foundSet, filterSet, true)
}
@@ -871,6 +1061,9 @@ func transposeWithCounts(input *BSI, filterSet *Bitmap, batch []uint64, resultsC
// Increment - In-place increment of values in a BSI. Found set select columns for incrementing.
func (b *BSI) Increment(foundSet *Bitmap) {
if foundSet == nil {
foundSet = &b.eBM
}
b.addDigit(foundSet, 0)
b.eBM.Or(foundSet)
}

View File

@@ -1,7 +1,7 @@
package roaring64
import (
"github.com/RoaringBitmap/roaring"
"github.com/RoaringBitmap/roaring/v2"
)
// IntIterable64 allows you to iterate over the values in a Bitmap

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"runtime"
"github.com/RoaringBitmap/roaring"
"github.com/RoaringBitmap/roaring/v2"
)
var defaultWorkerCount = runtime.NumCPU()
@@ -144,6 +144,8 @@ func (c parChunk) size() int {
return c.ra.size()
}
// parNaiveStartAt returns the index of the first key that is inclusive between start and last
// Returns the size if there is no such key
func parNaiveStartAt(ra *roaringArray64, start uint32, last uint32) int {
for idx, key := range ra.keys {
if key >= start && key <= last {
@@ -170,7 +172,6 @@ func orOnRange(ra1, ra2 *roaringArray64, start, last uint32) *roaringArray64 {
key2 = ra2.getKeyAtIndex(idx2)
for key1 <= last && key2 <= last {
if key1 < key2 {
answer.appendCopy(*ra1, idx1)
idx1++
@@ -188,7 +189,7 @@ func orOnRange(ra1, ra2 *roaringArray64, start, last uint32) *roaringArray64 {
} else {
c1 := ra1.getContainerAtIndex(idx1)
//answer.appendContainer(key1, c1.lazyOR(ra2.getContainerAtIndex(idx2)), false)
// answer.appendContainer(key1, c1.lazyOR(ra2.getContainerAtIndex(idx2)), false)
answer.appendContainer(key1, roaring.Or(c1, ra2.getContainerAtIndex(idx2)), false)
idx1++
idx2++
@@ -261,7 +262,7 @@ func iorOnRange(ra1, ra2 *roaringArray64, start, last uint32) *roaringArray64 {
} else {
c1 := ra1.getWritableContainerAtIndex(idx1)
//ra1.containers[idx1] = c1.lazyIOR(ra2.getContainerAtIndex(idx2))
// ra1.containers[idx1] = c1.lazyIOR(ra2.getContainerAtIndex(idx2))
c1.Or(ra2.getContainerAtIndex(idx2))
ra1.setContainerAtIndex(idx1, c1)

View File

@@ -8,12 +8,14 @@ import (
"io"
"strconv"
"github.com/RoaringBitmap/roaring"
"github.com/RoaringBitmap/roaring/internal"
"github.com/RoaringBitmap/roaring/v2"
"github.com/RoaringBitmap/roaring/v2/internal"
)
const serialCookieNoRunContainer = 12346 // only arrays and bitmaps
const serialCookie = 12347 // runs, arrays, and bitmaps
const (
serialCookieNoRunContainer = 12346 // only arrays and bitmaps
serialCookie = 12347 // runs, arrays, and bitmaps
)
// Bitmap represents a compressed bitmap where you can add integers.
type Bitmap struct {
@@ -25,7 +27,6 @@ func (rb *Bitmap) ToBase64() (string, error) {
buf := new(bytes.Buffer)
_, err := rb.WriteTo(buf)
return base64.StdEncoding.EncodeToString(buf.Bytes()), err
}
// FromBase64 deserializes a bitmap from Base64
@@ -52,7 +53,6 @@ func (rb *Bitmap) ToBytes() ([]byte, error) {
// implementations (Java, Go, C++) and it has a specification :
// https://github.com/RoaringBitmap/RoaringFormatSpec#extention-for-64-bit-implementations
func (rb *Bitmap) WriteTo(stream io.Writer) (int64, error) {
var n int64
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, uint64(rb.highlowcontainer.size()))
@@ -87,11 +87,10 @@ func (rb *Bitmap) WriteTo(stream io.Writer) (int64, error) {
func (rb *Bitmap) FromUnsafeBytes(data []byte) (p int64, err error) {
stream := internal.NewByteBuffer(data)
sizeBuf := make([]byte, 8)
n, err := stream.Read(sizeBuf)
_, err = stream.Read(sizeBuf)
if err != nil {
return 0, err
}
p += int64(n)
size := binary.LittleEndian.Uint64(sizeBuf)
rb.highlowcontainer.resize(0)
@@ -115,17 +114,16 @@ func (rb *Bitmap) FromUnsafeBytes(data []byte) (p int64, err error) {
if err != nil {
return 0, fmt.Errorf("error in bitmap.UnsafeFromBytes: could not read key #%d: %w", i, err)
}
p += 4
rb.highlowcontainer.keys[i] = binary.LittleEndian.Uint32(keyBuf)
rb.highlowcontainer.containers[i] = roaring.NewBitmap()
n, err := rb.highlowcontainer.containers[i].ReadFrom(stream)
if n == 0 || err != nil {
return int64(n), fmt.Errorf("Could not deserialize bitmap for key #%d: %s", i, err)
}
p += int64(n)
}
return p, nil
return stream.GetReadBytes(), nil
}
// ReadFrom reads a serialized version of this bitmap from stream.
@@ -167,23 +165,15 @@ func (rb *Bitmap) ReadFrom(stream io.Reader) (p int64, err error) {
rb.highlowcontainer.keys[i] = binary.LittleEndian.Uint32(keyBuf)
rb.highlowcontainer.containers[i] = roaring.NewBitmap()
n, err := rb.highlowcontainer.containers[i].ReadFrom(stream)
if n == 0 || err != nil {
return int64(n), fmt.Errorf("Could not deserialize bitmap for key #%d: %s", i, err)
}
p += int64(n)
}
return p, nil
}
// FromBuffer creates a bitmap from its serialized version stored in buffer
// func (rb *Bitmap) FromBuffer(data []byte) (p int64, err error) {
//
// // TODO: Add buffer interning as in base roaring package.
// buf := bytes.NewBuffer(data)
// return rb.ReadFrom(buf)
// }
// MarshalBinary implements the encoding.BinaryMarshaler interface for the bitmap
// (same as ToBytes)
func (rb *Bitmap) MarshalBinary() ([]byte, error) {
@@ -1251,6 +1241,10 @@ func (rb *Bitmap) GetSerializedSizeInBytes() uint64 {
return rb.highlowcontainer.serializedSizeInBytes()
}
func (rb *Bitmap) Validate() error {
return rb.highlowcontainer.validate()
}
// Roaring32AsRoaring64 inserts a 32-bit roaring bitmap into
// a 64-bit roaring bitmap. No copy is made.
func Roaring32AsRoaring64(bm32 *roaring.Bitmap) *Bitmap {

View File

@@ -1,7 +1,9 @@
package roaring64
import (
"github.com/RoaringBitmap/roaring"
"errors"
"github.com/RoaringBitmap/roaring/v2"
)
type roaringArray64 struct {
@@ -11,6 +13,11 @@ type roaringArray64 struct {
copyOnWrite bool
}
var (
ErrKeySortOrder = errors.New("keys were out of order")
ErrCardinalityConstraint = errors.New("size of arrays was not coherent")
)
// runOptimize compresses the element containers to minimize space consumed.
// Q: how does this interact with copyOnWrite and needCopyOnWrite?
// A: since we aren't changing the logical content, just the representation,
@@ -140,7 +147,6 @@ func (ra *roaringArray64) clear() {
}
func (ra *roaringArray64) clone() *roaringArray64 {
sa := roaringArray64{}
sa.copyOnWrite = ra.copyOnWrite
@@ -328,6 +334,15 @@ func (ra *roaringArray64) hasRunCompression() bool {
return false
}
/**
* Find the smallest integer index strictly larger than pos such that array[index].key&gt;=min. If none can
* be found, return size. Based on code by O. Kaser.
*
* @param min minimal value
* @param pos index to exceed
* @return the smallest index greater than pos such that array[index].key is at least as large as
* min, or size if it is not possible.
*/
func (ra *roaringArray64) advanceUntil(min uint32, pos int) int {
lower := pos + 1
@@ -401,3 +416,47 @@ func (ra *roaringArray64) serializedSizeInBytes() uint64 {
}
return answer
}
func (ra *roaringArray64) checkKeysSorted() bool {
if len(ra.keys) == 0 || len(ra.keys) == 1 {
return true
}
previous := ra.keys[0]
for nextIdx := 1; nextIdx < len(ra.keys); nextIdx++ {
next := ra.keys[nextIdx]
if previous >= next {
return false
}
previous = next
}
return true
}
// validate checks the referential integrity
// ensures len(keys) == len(containers), recurses and checks each container type
func (ra *roaringArray64) validate() error {
if !ra.checkKeysSorted() {
return ErrKeySortOrder
}
if len(ra.keys) != len(ra.containers) {
return ErrCardinalityConstraint
}
if len(ra.keys) != len(ra.needCopyOnWrite) {
return ErrCardinalityConstraint
}
for _, maps := range ra.containers {
err := maps.Validate()
if err != nil {
return err
}
if maps.IsEmpty() {
return errors.New("empty container")
}
}
return nil
}

View File

@@ -1,6 +1,6 @@
package roaring64
import "github.com/RoaringBitmap/roaring"
import "github.com/RoaringBitmap/roaring/v2"
func highbits(x uint64) uint32 {
return uint32(x >> 32)

View File

@@ -3,10 +3,11 @@ package roaring
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/RoaringBitmap/roaring/internal"
"github.com/RoaringBitmap/roaring/v2/internal"
)
type container interface {
@@ -30,7 +31,6 @@ type container interface {
iadd(x uint16) bool // inplace, returns true if x was new.
iaddReturnMinimized(uint16) container // may change return type to minimize storage.
//addRange(start, final int) container // range is [firstOfRange,lastOfRange) (unused)
iaddRange(start, endx int) container // i stands for inplace, range is [firstOfRange,endx)
iremove(x uint16) bool // inplace, returns true if x was present.
@@ -61,7 +61,6 @@ type container interface {
lazyOR(r container) container
lazyIOR(r container) container
getSizeInBytes() int
//removeRange(start, final int) container // range is [firstOfRange,lastOfRange) (unused)
iremoveRange(start, final int) container // i stands for inplace, range is [firstOfRange,lastOfRange)
selectInt(x uint16) int // selectInt returns the xth integer in the container
serializedSizeInBytes() int
@@ -71,6 +70,14 @@ type container interface {
toEfficientContainer() container
String() string
containerType() contype
safeMinimum() (uint16, error)
safeMaximum() (uint16, error)
nextValue(x uint16) int
previousValue(x uint16) int
nextAbsentValue(x uint16) int
previousAbsentValue(x uint16) int
validate() error
}
type contype uint8
@@ -82,6 +89,11 @@ const (
run32Contype
)
var (
ErrKeySortOrder = errors.New("keys were out of order")
ErrCardinalityConstraint = errors.New("size of arrays was not coherent")
)
// careful: range is [firstOfRange,lastOfRange]
func rangeOfOnes(start, last int) container {
if start > MaxUint16 {
@@ -178,7 +190,6 @@ func (ra *roaringArray) appendCopiesUntil(sa roaringArray, stoppingKey uint16) {
} else {
// since there is no copy-on-write, we need to clone the container (this is important)
ra.appendContainer(sa.keys[i], sa.containers[i].clone(), thiscopyonewrite)
}
}
}
@@ -204,7 +215,6 @@ func (ra *roaringArray) appendCopiesAfter(sa roaringArray, beforeStart uint16) {
} else {
// since there is no copy-on-write, we need to clone the container (this is important)
ra.appendContainer(sa.keys[i], sa.containers[i].clone(), thiscopyonewrite)
}
}
}
@@ -239,7 +249,6 @@ func (ra *roaringArray) clear() {
}
func (ra *roaringArray) clone() *roaringArray {
sa := roaringArray{}
sa.copyOnWrite = ra.copyOnWrite
@@ -288,6 +297,8 @@ func (ra *roaringArray) cloneCopyOnWriteContainers() {
// return (ra.binarySearch(0, int64(len(ra.keys)), x) >= 0)
//}
// getContainer returns the container with key `x`
// if no such container exists `nil` is returned
func (ra *roaringArray) getContainer(x uint16) container {
i := ra.binarySearch(0, int64(len(ra.keys)), x)
if i < 0 {
@@ -325,7 +336,6 @@ func (ra *roaringArray) getUnionedWritableContainer(pos int, other container) co
return ra.getContainerAtIndex(pos).or(other)
}
return ra.getContainerAtIndex(pos).ior(other)
}
func (ra *roaringArray) getWritableContainerAtIndex(i int) container {
@@ -336,7 +346,10 @@ func (ra *roaringArray) getWritableContainerAtIndex(i int) container {
return ra.containers[i]
}
// getIndex returns the index of the container with key `x`
// if no such container exists a negative value is returned
func (ra *roaringArray) getIndex(x uint16) int {
// Todo : test
// before the binary search, we optimize for frequent cases
size := len(ra.keys)
if (size == 0) || (ra.keys[size-1] == x) {
@@ -396,7 +409,10 @@ func (ra *roaringArray) size() int {
return len(ra.keys)
}
// binarySearch returns the index of the key.
// negative value returned if not found
func (ra *roaringArray) binarySearch(begin, end int64, ikey uint16) int {
// TODO: add unit tests
low := begin
high := end - 1
for low+16 <= high {
@@ -455,7 +471,6 @@ func (ra *roaringArray) headerSize() uint64 {
return 4 + (size+7)/8 + 8*size // - 4 because we pack the size with the cookie
}
return 4 + 4 + 8*size
}
// should be dirt cheap
@@ -489,7 +504,7 @@ func (ra *roaringArray) writeTo(w io.Writer) (n int64, err error) {
binary.LittleEndian.PutUint16(buf[2:], uint16(len(ra.keys)-1))
nw += 2
// compute isRun bitmap without temporary allocation
var runbitmapslice = buf[nw : nw+isRunSizeInBytes]
runbitmapslice := buf[nw : nw+isRunSizeInBytes]
for i, c := range ra.containers {
switch c.(type) {
case *runContainer16:
@@ -577,7 +592,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
// create is-run-container bitmap
isRunBitmapSize := (int(size) + 7) / 8
isRunBitmap, err = stream.Next(isRunBitmapSize)
if err != nil {
return stream.GetReadBytes(), fmt.Errorf("malformed bitmap, failed to read is-run bitmap, got: %s", err)
}
@@ -596,7 +610,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
// descriptive header
buf, err := stream.Next(2 * 2 * int(size))
if err != nil {
return stream.GetReadBytes(), fmt.Errorf("failed to read descriptive header: %s", err)
}
@@ -637,13 +650,11 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
if isRunBitmap != nil && isRunBitmap[i/8]&(1<<(i%8)) != 0 {
// run container
nr, err := stream.ReadUInt16()
if err != nil {
return 0, fmt.Errorf("failed to read runtime container size: %s", err)
}
buf, err := stream.Next(int(nr) * 4)
if err != nil {
return stream.GetReadBytes(), fmt.Errorf("failed to read runtime container content: %s", err)
}
@@ -656,7 +667,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
} else if card > arrayDefaultMaxSize {
// bitmap container
buf, err := stream.Next(arrayDefaultMaxSize * 2)
if err != nil {
return stream.GetReadBytes(), fmt.Errorf("failed to read bitmap container: %s", err)
}
@@ -670,7 +680,6 @@ func (ra *roaringArray) readFrom(stream internal.ByteInput, cookieHeader ...byte
} else {
// array container
buf, err := stream.Next(card * 2)
if err != nil {
return stream.GetReadBytes(), fmt.Errorf("failed to read array container: %s", err)
}
@@ -696,6 +705,15 @@ func (ra *roaringArray) hasRunCompression() bool {
return false
}
/**
* Find the smallest integer index larger than pos such that array[index].key&gt;=min. If none can
* be found, return size. Based on code by O. Kaser.
*
* @param min minimal value
* @param pos index to exceed
* @return the smallest index greater than pos such that array[index].key is at least as large as
* min, or size if it is not possible.
*/
func (ra *roaringArray) advanceUntil(min uint16, pos int) int {
lower := pos + 1
@@ -759,3 +777,44 @@ func (ra *roaringArray) needsCopyOnWrite(i int) bool {
func (ra *roaringArray) setNeedsCopyOnWrite(i int) {
ra.needCopyOnWrite[i] = true
}
func (ra *roaringArray) checkKeysSorted() bool {
if len(ra.keys) == 0 || len(ra.keys) == 1 {
return true
}
previous := ra.keys[0]
for nextIdx := 1; nextIdx < len(ra.keys); nextIdx++ {
next := ra.keys[nextIdx]
if previous >= next {
return false
}
previous = next
}
return true
}
// validate checks the referential integrity
// ensures len(keys) == len(containers), recurses and checks each container type
func (ra *roaringArray) validate() error {
if !ra.checkKeysSorted() {
return ErrKeySortOrder
}
if len(ra.keys) != len(ra.containers) {
return ErrCardinalityConstraint
}
if len(ra.keys) != len(ra.needCopyOnWrite) {
return ErrCardinalityConstraint
}
for _, container := range ra.containers {
err := container.validate()
if err != nil {
return err
}
}
return nil
}

View File

@@ -39,9 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import (
"errors"
"fmt"
"sort"
"unsafe"
)
// runContainer16 does run-length encoding of sets of
@@ -59,6 +59,16 @@ type interval16 struct {
length uint16 // length minus 1
}
var (
ErrRunIntervalsEmpty = errors.New("run contained no interval")
ErrRunNonSorted = errors.New("runs were not sorted")
ErrRunIntervalEqual = errors.New("intervals were equal")
ErrRunIntervalOverlap = errors.New("intervals overlapped or were continguous")
ErrRunIntervalSize = errors.New("too many intervals relative to data")
MaxNumIntervals = 2048
MaxIntervalsSum = 2048
)
func newInterval16Range(start, last uint16) interval16 {
if last < start {
panic(fmt.Sprintf("last (%d) cannot be smaller than start (%d)", last, start))
@@ -201,7 +211,6 @@ func newRunContainer16FromVals(alreadySorted bool, vals ...uint16) *runContainer
// somewhat efficiently. For reference, see the Java
// https://github.com/RoaringBitmap/RoaringBitmap/blob/master/src/main/java/org/roaringbitmap/RunContainer.java#L145-L192
func newRunContainer16FromBitmapContainer(bc *bitmapContainer) *runContainer16 {
rc := &runContainer16{}
nbrRuns := bc.numberOfRuns()
if nbrRuns == 0 {
@@ -251,7 +260,6 @@ func newRunContainer16FromBitmapContainer(bc *bitmapContainer) *runContainer16 {
curWord = curWordWith1s & (curWordWith1s + 1)
// We've lathered and rinsed, so repeat...
}
}
// newRunContainer16FromArray populates a new
@@ -293,7 +301,6 @@ func newRunContainer16FromArray(arr *arrayContainer) *runContainer16 {
// If you have a small number of additions to an already
// big runContainer16, calling Add() may be faster.
func (rc *runContainer16) set(alreadySorted bool, vals ...uint16) {
rc2 := newRunContainer16FromVals(alreadySorted, vals...)
un := rc.union(rc2)
rc.iv = un.iv
@@ -374,7 +381,6 @@ func intersectInterval16s(a, b interval16) (res interval16, isEmpty bool) {
// union merges two runContainer16s, producing
// a new runContainer16 with the union of rc and b.
func (rc *runContainer16) union(b *runContainer16) *runContainer16 {
// rc is also known as 'a' here, but golint insisted we
// call it rc for consistency with the rest of the methods.
@@ -457,7 +463,6 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 {
break aAdds
}
}
}
if !bDone {
@@ -471,7 +476,6 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 {
break bAdds
}
}
}
m = append(m, merged)
@@ -489,7 +493,6 @@ func (rc *runContainer16) union(b *runContainer16) *runContainer16 {
// unionCardinality returns the cardinality of the merger of two runContainer16s, the union of rc and b.
func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
// rc is also known as 'a' here, but golint insisted we
// call it rc for consistency with the rest of the methods.
answer := uint(0)
@@ -528,7 +531,7 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
}
if !mergedUpdated {
// we know that merged is disjoint from cura and curb
//m = append(m, merged)
// m = append(m, merged)
answer += uint(merged.last()) - uint(merged.start) + 1
mergedUsed = false
}
@@ -539,11 +542,11 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
if !canMerge16(cura, curb) {
if cura.start < curb.start {
answer += uint(cura.last()) - uint(cura.start) + 1
//m = append(m, cura)
// m = append(m, cura)
na++
} else {
answer += uint(curb.last()) - uint(curb.start) + 1
//m = append(m, curb)
// m = append(m, curb)
nb++
}
} else {
@@ -574,7 +577,6 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
break aAdds
}
}
}
if !bDone {
@@ -588,10 +590,9 @@ func (rc *runContainer16) unionCardinality(b *runContainer16) uint {
break bAdds
}
}
}
//m = append(m, merged)
// m = append(m, merged)
answer += uint(merged.last()) - uint(merged.start) + 1
}
for _, r := range rc.iv[na:] {
@@ -615,7 +616,6 @@ func (rc *runContainer16) indexOfIntervalAtOrAfter(key int, startIndex int) int
// intersect returns a new runContainer16 holding the
// intersection of rc (also known as 'a') and b.
func (rc *runContainer16) intersect(b *runContainer16) *runContainer16 {
a := rc
numa := int(len(a.iv))
numb := int(len(b.iv))
@@ -645,8 +645,7 @@ func (rc *runContainer16) intersect(b *runContainer16) *runContainer16 {
toploop:
for acuri < numa && bcuri < numb {
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection =
intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last()))
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection = intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last()))
if !isOverlap {
switch {
@@ -664,7 +663,6 @@ toploop:
}
bstart = int(b.iv[bcuri].start)
}
} else {
// isOverlap
output = append(output, intersection)
@@ -748,8 +746,7 @@ toploop:
for acuri < numa && bcuri < numb {
pass++
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection =
intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last()))
isOverlap, isLeftoverA, isLeftoverB, leftoverstart, intersection = intersectWithLeftover16(astart, int(a.iv[acuri].last()), bstart, int(b.iv[bcuri].last()))
if !isOverlap {
switch {
@@ -767,7 +764,6 @@ toploop:
}
bstart = int(b.iv[bcuri].start)
}
} else {
// isOverlap
answer += int(intersection.last()) - int(intersection.start) + 1
@@ -941,7 +937,7 @@ func (rc *runContainer16) searchRange(key int, startIndex int, endxIndex int) (w
// b) whichInterval16 == -1 if key is before our first
// interval16 in rc.iv;
//
// c) whichInterval16 is set to the minimum index of rc.iv
// c) whichInterval16 is set to the maximum index of rc.iv
// which comes strictly before the key;
// so rc.iv[whichInterval16].last < key,
// and if whichInterval16+1 exists, then key < rc.iv[whichInterval16+1].start
@@ -1014,10 +1010,10 @@ func newRunContainer16TakeOwnership(iv []interval16) *runContainer16 {
return rc
}
const baseRc16Size = int(unsafe.Sizeof(runContainer16{}))
const perIntervalRc16Size = int(unsafe.Sizeof(interval16{}))
const baseDiskRc16Size = int(unsafe.Sizeof(uint16(0)))
const (
baseRc16Size = 2
perIntervalRc16Size = 4
)
// see also runContainer16SerializedSizeInBytes(numRuns int) int
@@ -1030,7 +1026,7 @@ func (rc *runContainer16) getSizeInBytes() int {
// runContainer16SerializedSizeInBytes returns the number of bytes of disk
// required to hold numRuns in a runContainer16.
func runContainer16SerializedSizeInBytes(numRuns int) int {
return perIntervalRc16Size*numRuns + baseDiskRc16Size
return perIntervalRc16Size*numRuns + baseRc16Size
}
// Add adds a single value k to the set.
@@ -1274,7 +1270,7 @@ func (ri *runIterator16) nextMany(hs uint32, buf []uint32) int {
break
}
} else {
ri.curPosInIndex += uint16(moreVals) //moreVals always fits in uint16
ri.curPosInIndex += uint16(moreVals) // moreVals always fits in uint16
}
}
@@ -1315,7 +1311,7 @@ func (ri *runIterator16) nextMany64(hs uint64, buf []uint64) int {
break
}
} else {
ri.curPosInIndex += uint16(moreVals) //moreVals always fits in uint16
ri.curPosInIndex += uint16(moreVals) // moreVals always fits in uint16
}
}
@@ -1324,7 +1320,6 @@ func (ri *runIterator16) nextMany64(hs uint64, buf []uint64) int {
// remove removes key from the container.
func (rc *runContainer16) removeKey(key uint16) (wasPresent bool) {
var index int
index, wasPresent, _ = rc.search(int(key))
if !wasPresent {
@@ -1361,7 +1356,7 @@ func (rc *runContainer16) deleteAt(curIndex *int, curPosInIndex *uint16) {
*curPosInIndex--
// if we leave *curIndex alone, then Next() will work properly even after the delete.
default:
//middle
// middle
// split into two, adding an interval16
new0 := newInterval16Range(rc.iv[ci].start, rc.iv[ci].start+*curPosInIndex-1)
@@ -1376,7 +1371,6 @@ func (rc *runContainer16) deleteAt(curIndex *int, curPosInIndex *uint16) {
*curIndex++
*curPosInIndex = 0
}
}
func have4Overlap16(astart, alast, bstart, blast int) bool {
@@ -1503,6 +1497,26 @@ func (iv interval16) isSuperSetOf(b interval16) bool {
return iv.start <= b.start && b.last() <= iv.last()
}
func (iv interval16) isNonContiguousDisjoint(b interval16) bool {
// cover the zero start case
if iv.start == b.start {
return false
}
nonContiguous1 := uint32(iv.start) == uint32(b.last())+1 || uint32(iv.last()) == uint32(b.start)+1
nonContiguous2 := uint32(b.start) == uint32(iv.last())+1 || uint32(b.last()) == uint32(iv.start)+1
if nonContiguous1 || nonContiguous2 {
return false
}
ivl := iv.last()
bl := b.last()
c1 := iv.start <= b.start && b.start <= ivl
c2 := b.start <= iv.start && iv.start <= bl
return !c1 && !c2
}
func (iv interval16) subtractInterval(del interval16) (left []interval16, delcount int) {
isect, isEmpty := intersectInterval16s(iv, del)
@@ -1678,7 +1692,6 @@ func (rc *runContainer16) isubtract(del interval16) {
// port of run_container_andnot from CRoaring...
// https://github.com/RoaringBitmap/CRoaring/blob/master/src/containers/run.c#L435-L496
func (rc *runContainer16) AndNotRunContainer16(b *runContainer16) *runContainer16 {
if len(b.iv) == 0 || len(rc.iv) == 0 {
return rc
}
@@ -1781,10 +1794,25 @@ func (rc *runContainer16) minimum() uint16 {
return rc.iv[0].start // assume not empty
}
func (rc *runContainer16) safeMinimum() (uint16, error) {
if len(rc.iv) == 0 {
return 0, errors.New("Empty runs")
}
return rc.minimum(), nil
}
func (rc *runContainer16) maximum() uint16 {
return rc.iv[len(rc.iv)-1].last() // assume not empty
}
func (rc *runContainer16) safeMaximum() (uint16, error) {
if len(rc.iv) == 0 {
return 0, errors.New("Empty runs")
}
return rc.maximum(), nil // assume not empty
}
func (rc *runContainer16) isFull() bool {
return (len(rc.iv) == 1) && ((rc.iv[0].start == 0) && (rc.iv[0].last() == MaxUint16))
}
@@ -1949,7 +1977,6 @@ func (rc *runContainer16) getManyIterator() manyIterable {
// add the values in the range [firstOfRange, endx). endx
// is still abe to express 2^16 because it is an int not an uint16.
func (rc *runContainer16) iaddRange(firstOfRange, endx int) container {
if firstOfRange > endx {
panic(fmt.Sprintf("invalid %v = endx > firstOfRange", endx))
}
@@ -2002,7 +2029,6 @@ func (rc *runContainer16) not(firstOfRange, endx int) container {
// makes 2 more passes through the arrays than should be
// strictly necessary. Measure both ways though--this may not matter.
func (rc *runContainer16) Not(firstOfRange, endx int) *runContainer16 {
if firstOfRange > endx {
panic(fmt.Sprintf("invalid %v = endx > firstOfRange == %v", endx, firstOfRange))
}
@@ -2066,12 +2092,12 @@ func (rc *runContainer16) equals(o container) bool {
rit := rc.getShortIterator()
bit := o.getShortIterator()
//k := 0
// k := 0
for rit.hasNext() {
if bit.next() != rit.next() {
return false
}
//k++
// k++
}
return true
}
@@ -2132,7 +2158,7 @@ func (rc *runContainer16) andBitmapContainerCardinality(bc *bitmapContainer) int
for i := range rc.iv {
answer += bc.getCardinalityInRange(uint(rc.iv[i].start), uint(rc.iv[i].last())+1)
}
//bc.computeCardinality()
// bc.computeCardinality()
return answer
}
@@ -2150,7 +2176,7 @@ func (rc *runContainer16) orArray(ac *arrayContainer) container {
}
intervals, cardMinusOne := runArrayUnionToRuns(rc, ac)
result := newRunContainer16TakeOwnership(intervals)
if len(intervals) >= 2048 && cardMinusOne >= arrayDefaultMaxSize {
if len(intervals) >= MaxNumIntervals && cardMinusOne >= arrayDefaultMaxSize {
return newBitmapContainerFromRun(result)
}
if len(intervals)*2 > 1+int(cardMinusOne) {
@@ -2190,7 +2216,6 @@ func (rc *runContainer16) inplaceUnion(rc2 *runContainer16) container {
}
func (rc *runContainer16) iorBitmapContainer(bc *bitmapContainer) container {
it := bc.getShortIterator()
for it.hasNext() {
rc.Add(it.next())
@@ -2206,11 +2231,11 @@ func (rc *runContainer16) iorArray(ac *arrayContainer) container {
return rc
}
var cardMinusOne uint16
//TODO: perform the union algorithm in-place using rc.iv
// TODO: perform the union algorithm in-place using rc.iv
// this can be done with methods like the in-place array container union
// but maybe lazily moving the remaining elements back.
rc.iv, cardMinusOne = runArrayUnionToRuns(rc, ac)
if len(rc.iv) >= 2048 && cardMinusOne >= arrayDefaultMaxSize {
if len(rc.iv) >= MaxNumIntervals && cardMinusOne >= arrayDefaultMaxSize {
return newBitmapContainerFromRun(rc)
}
if len(rc.iv)*2 > 1+int(cardMinusOne) {
@@ -2438,12 +2463,8 @@ func (rc *runContainer16) toBitmapContainer() *bitmapContainer {
}
func (rc *runContainer16) iandNotRunContainer16(x2 *runContainer16) container {
rcb := rc.toBitmapContainer()
x2b := x2.toBitmapContainer()
rcb.iandNotBitmapSurely(x2b)
// TODO: check size and optimize the return value
// TODO: is inplace modification really required? If not, elide the copy.
rc2 := newRunContainer16FromBitmapContainer(rcb)
rc2 := rc.AndNotRunContainer16(x2)
*rc = *rc2
return rc
}
@@ -2492,7 +2513,7 @@ func (rc *runContainer16) toEfficientContainer() container {
sizeAsBitmapContainer := bitmapContainerSizeInBytes()
card := rc.getCardinality()
sizeAsArrayContainer := arrayContainerSizeInBytes(card)
if sizeAsRunContainer <= minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
if sizeAsRunContainer < minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
return rc
}
if card <= arrayDefaultMaxSize {
@@ -2511,7 +2532,6 @@ func (rc *runContainer16) toArrayContainer() *arrayContainer {
}
func newRunContainer16FromContainer(c container) *runContainer16 {
switch x := c.(type) {
case *runContainer16:
return x.Clone()
@@ -2622,3 +2642,169 @@ func (rc *runContainer16) addOffset(x uint16) (container, container) {
return low, high
}
// nextValue returns either the `target` if found or the next larger value.
// If the target is in the interior or a run then `target` will be returned
// Ex: If our run structure resmembles [[a,c], [d,f]] with a <= target <= c then `target` will be returned.
// Ex: If c < target < d then d is returned.
// Ex: If target < a then a is returned
// if the target > max, this is out of bounds and -1 is returned
func (rc *runContainer16) nextValue(target uint16) int {
if len(rc.iv) == 0 {
return -1
}
whichIndex, alreadyPresent, _ := rc.search(int(target))
if alreadyPresent {
return int(target)
}
if whichIndex == -1 {
return int(rc.iv[0].start)
}
if whichIndex == len(rc.iv)-1 {
return -1
}
// The if relies on the non-contiguous nature of runs.
// If we have two runs [a,b] and another run [c,d]
// We can rely on the invariant that b+1 < c
// We will return c
possibleNext := whichIndex + 1
if possibleNext < len(rc.iv) {
return int(rc.iv[possibleNext].start)
}
return -1
}
// nextAbsentValue returns the next absent value.
// By construction the next absent value will be located between gaps in runs
//
// Ex: if our runs resemble [[a,b],[c,d]] and a <= target <= b then b+1 will not be equal to c, b+1 will be returned
// Ex: if target < a then target is returned
// Ex: if target > d then target is returned
func (rc *runContainer16) nextAbsentValue(target uint16) int {
whichIndex, alreadyPresent, _ := rc.search(int(target))
if !alreadyPresent {
return int(target)
}
return int(rc.iv[whichIndex].last()) + 1
}
// previousValue will return the previous present value
// If the target is in the interior of a run then `target` will be returned
//
// Example:
// If our run structure resmembles [[a,c], [d,f]] with a <= target <= c then target will be returned.
// If c < target < d then c is returned.
// if target > f then f is returned
// if the target is less than a, this is out of bounds and -1 is returned
func (rc *runContainer16) previousValue(target uint16) int {
whichIndex, alreadyPresent, _ := rc.search(int(target))
if len(rc.iv) == 0 {
return int(target)
}
if alreadyPresent {
return int(target)
}
if whichIndex == -1 {
return -1
}
return int(rc.iv[whichIndex].last())
}
// previousAbsentValue will return the previous absent value
// If the target is in the interior of a run then then the start of the range minus 1 will be returned
//
// Example:
// If our run structure resmembles [[x,z], [a,c], [d,f]] with a <= target <= c then a-1 will be returned.
// if the target < x then target is returned
// if target > f then target is returned
func (rc *runContainer16) previousAbsentValue(target uint16) int {
whichIndex, alreadyPresent, _ := rc.search(int(target))
if !alreadyPresent {
return int(target)
}
return int(rc.iv[whichIndex].start) - 1
}
// isNonContiguousDisjoint returns an error if the intervals overlap e.g have non-empty intersection
func isNonContiguousDisjoint(outer interval16, inner interval16) error {
if !outer.isNonContiguousDisjoint(inner) {
return ErrRunIntervalOverlap
}
return nil
}
// validate checks the run container referential integrity
// Ensures runs are not degenerate, non-contiguous and non-overlapping
func (rc *runContainer16) validate() error {
if rc.getCardinality() == 0 {
return ErrRunIntervalsEmpty
}
intervalsSum := 0
for outeridx := range rc.iv {
// The length being stored is the actual length - 1.
// So we need to add 1 to get the actual length.
// It is not possible to have a run with length 0.
outerInterval := rc.iv[outeridx]
intervalsSum += outerInterval.runlen()
for inneridx := outeridx + 1; inneridx < len(rc.iv); inneridx++ {
innerInterval := rc.iv[inneridx]
if outerInterval.equal(innerInterval) {
return ErrRunIntervalEqual
}
// only check the start of runs
// if the run length overlap the next check will catch that.
if outerInterval.start >= innerInterval.start {
return ErrRunNonSorted
}
err := isNonContiguousDisjoint(outerInterval, innerInterval)
if err != nil {
return err
}
}
}
/*
if number of distinct values in the container >= 2048 then
check that the number of runs is no more than 2047
(otherwise you could use a bitset container)
else
check that the number of runs < (number of distinct values) / 2
(otherwise you could use an array container)
*/
sizeAsRunContainer := runContainer16SerializedSizeInBytes(len(rc.iv))
sizeAsBitmapContainer := bitmapContainerSizeInBytes()
sizeAsArrayContainer := arrayContainerSizeInBytes(intervalsSum)
// this is always ok:
if sizeAsRunContainer < minOfInt(sizeAsBitmapContainer, sizeAsArrayContainer) {
return nil
}
if sizeAsRunContainer >= sizeAsBitmapContainer {
return ErrRunIntervalSize
}
if sizeAsRunContainer >= sizeAsArrayContainer {
return ErrRunIntervalSize
}
return nil
}

View File

@@ -299,6 +299,15 @@ func (rb *Bitmap) FrozenView(buf []byte) error {
return rb.highlowcontainer.frozenView(buf)
}
func (rb *Bitmap) MustFrozenView(buf []byte) error {
if err := rb.FrozenView(buf); err != nil {
return err
}
err := rb.Validate()
return err
}
/* Verbatim specification from CRoaring.
*
* FROZEN SERIALIZATION FORMAT DESCRIPTION

View File

@@ -1,26 +1,12 @@
package roaring
func equal(a, b []uint16) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func difference(set1 []uint16, set2 []uint16, buffer []uint16) int {
if 0 == len(set2) {
if len(set2) == 0 {
buffer = buffer[:len(set1)]
for k := 0; k < len(set1); k++ {
buffer[k] = set1[k]
}
copy(buffer, set1)
return len(set1)
}
if 0 == len(set1) {
if len(set1) == 0 {
return 0
}
pos := 0
@@ -66,7 +52,6 @@ func difference(set1 []uint16, set2 []uint16, buffer []uint16) int {
}
}
return pos
}
func exclusiveUnion2by2(set1 []uint16, set2 []uint16, buffer []uint16) int {
@@ -135,6 +120,7 @@ func exclusiveUnion2by2(set1 []uint16, set2 []uint16, buffer []uint16) int {
return pos
}
// union2by2Cardinality computes the cardinality of the union
func union2by2Cardinality(set1 []uint16, set2 []uint16) int {
pos := 0
k1 := 0
@@ -186,8 +172,8 @@ func union2by2Cardinality(set1 []uint16, set2 []uint16) int {
func intersection2by2(
set1 []uint16,
set2 []uint16,
buffer []uint16) int {
buffer []uint16,
) int {
if len(set1)*64 < len(set2) {
return onesidedgallopingintersect2by2(set1, set2, buffer)
} else if len(set2)*64 < len(set1) {
@@ -197,10 +183,11 @@ func intersection2by2(
}
}
// intersection2by2Cardinality computes the cardinality of the intersection
func intersection2by2Cardinality(
set1 []uint16,
set2 []uint16) int {
set2 []uint16,
) int {
if len(set1)*64 < len(set2) {
return onesidedgallopingintersect2by2Cardinality(set1, set2)
} else if len(set2)*64 < len(set1) {
@@ -210,44 +197,45 @@ func intersection2by2Cardinality(
}
}
// intersects2by2 computes whether the two sets intersect
func intersects2by2(
set1 []uint16,
set2 []uint16) bool {
set2 []uint16,
) bool {
// could be optimized if one set is much larger than the other one
if (0 == len(set1)) || (0 == len(set2)) {
if (len(set1) == 0) || (len(set2) == 0) {
return false
}
k1 := 0
k2 := 0
s1 := set1[k1]
s2 := set2[k2]
index1 := 0
index2 := 0
value1 := set1[index1]
value2 := set2[index2]
mainwhile:
for {
if s2 < s1 {
if value2 < value1 {
for {
k2++
if k2 == len(set2) {
index2++
if index2 == len(set2) {
break mainwhile
}
s2 = set2[k2]
if s2 >= s1 {
value2 = set2[index2]
if value2 >= value1 {
break
}
}
}
if s1 < s2 {
if value1 < value2 {
for {
k1++
if k1 == len(set1) {
index1++
if index1 == len(set1) {
break mainwhile
}
s1 = set1[k1]
if s1 >= s2 {
value1 = set1[index1]
if value1 >= value2 {
break
}
}
} else {
// (set2[k2] == set1[k1])
return true
@@ -259,9 +247,9 @@ mainwhile:
func localintersect2by2(
set1 []uint16,
set2 []uint16,
buffer []uint16) int {
if (0 == len(set1)) || (0 == len(set2)) {
buffer []uint16,
) int {
if (len(set1) == 0) || (len(set2) == 0) {
return 0
}
k1 := 0
@@ -295,7 +283,6 @@ mainwhile:
break
}
}
} else {
// (set2[k2] == set1[k1])
buffer[pos] = s1
@@ -315,57 +302,57 @@ mainwhile:
return pos
}
// / localintersect2by2Cardinality computes the cardinality of the intersection
func localintersect2by2Cardinality(
set1 []uint16,
set2 []uint16) int {
if (0 == len(set1)) || (0 == len(set2)) {
set2 []uint16,
) int {
if (len(set1) == 0) || (len(set2) == 0) {
return 0
}
k1 := 0
k2 := 0
index1 := 0
index2 := 0
pos := 0
s1 := set1[k1]
s2 := set2[k2]
value1 := set1[index1]
value2 := set2[index2]
mainwhile:
for {
if s2 < s1 {
if value2 < value1 {
for {
k2++
if k2 == len(set2) {
index2++
if index2 == len(set2) {
break mainwhile
}
s2 = set2[k2]
if s2 >= s1 {
value2 = set2[index2]
if value2 >= value1 {
break
}
}
}
if s1 < s2 {
if value1 < value2 {
for {
k1++
if k1 == len(set1) {
index1++
if index1 == len(set1) {
break mainwhile
}
s1 = set1[k1]
if s1 >= s2 {
value1 = set1[index1]
if value1 >= value2 {
break
}
}
} else {
// (set2[k2] == set1[k1])
pos++
k1++
if k1 == len(set1) {
index1++
if index1 == len(set1) {
break
}
s1 = set1[k1]
k2++
if k2 == len(set2) {
value1 = set1[index1]
index2++
if index2 == len(set2) {
break
}
s2 = set2[k2]
value2 = set2[index2]
}
}
return pos
@@ -375,7 +362,8 @@ func advanceUntil(
array []uint16,
pos int,
length int,
min uint16) int {
min uint16,
) int {
lower := pos + 1
if lower >= length || array[lower] >= min {
@@ -423,14 +411,13 @@ func advanceUntil(
}
}
return upper
}
func onesidedgallopingintersect2by2(
smallset []uint16,
largeset []uint16,
buffer []uint16) int {
buffer []uint16,
) int {
if 0 == len(smallset) {
return 0
}
@@ -478,8 +465,8 @@ mainwhile:
func onesidedgallopingintersect2by2Cardinality(
smallset []uint16,
largeset []uint16) int {
largeset []uint16,
) int {
if 0 == len(smallset) {
return 0
}
@@ -548,3 +535,131 @@ func binarySearch(array []uint16, ikey uint16) int {
}
return -(low + 1)
}
// searchResult provides information about a search request.
// The values will depend on the context of the search
type searchResult struct {
value uint16
index int
exactMatch bool
}
// notFound returns a bool depending the search context
// For cases `previousValue` and `nextValue` if target is present in the slice
// this function will return `true` otherwise `false`
// For `nextAbsentValue` and `previousAbsentValue` this will only return `False`
func (sr *searchResult) notFound() bool {
return !sr.exactMatch
}
// outOfBounds indicates whether the target was outside the lower and upper bounds of the container
func (sr *searchResult) outOfBounds() bool {
return sr.index <= -1
}
// binarySearchUntil is a helper function around binarySearchUntilWithBounds
// The user does not have to pass in the lower and upper bound
// The lower bound is taken to be `0` and the upper bound `len(array)-1`
func binarySearchUntil(array []uint16, target uint16) searchResult {
return binarySearchUntilWithBounds(array, target, 0, len(array)-1)
}
// binarySearchUntilWithBounds returns a `searchResult`.
// If an exact match is found the `searchResult{target, <index>, true}` will be returned, where `<index>` is
// `target`s index in `array`, and `result.notFound()` evaluates to `false`.
// If a match is not found, but `target` was in-bounds then the result.index will be the closest smaller value
// Example: [ 8,9,11,12] if the target was 10, then `searchResult{9, 1, false}` will be returned.
// If `target` was out of bounds `searchResult{0, -1, false}` will be returned.
func binarySearchUntilWithBounds(array []uint16, target uint16, lowIndex int, maxIndex int) searchResult {
highIndex := maxIndex
closestIndex := -1
if target < array[lowIndex] {
return searchResult{0, closestIndex, false}
}
if target > array[maxIndex] {
return searchResult{0, len(array), false}
}
for lowIndex <= highIndex {
middleIndex := (lowIndex + highIndex) / 2
middleValue := array[middleIndex]
if middleValue == target {
return searchResult{middleValue, middleIndex, true}
}
if target < middleValue {
if middleIndex > 0 && target > array[middleIndex-1] {
return searchResult{array[middleIndex-1], middleIndex - 1, false}
}
highIndex = middleIndex
} else {
if middleIndex < maxIndex && target < array[middleIndex+1] {
return searchResult{middleValue, middleIndex, false}
}
lowIndex = middleIndex + 1
}
}
return searchResult{array[closestIndex], closestIndex, false}
}
// binarySearchPast is a wrapper around binarySearchPastWithBounds
// The user does not have to pass in the lower and upper bound
// The lower bound is taken to be `0` and the upper bound `len(array)-1`
func binarySearchPast(array []uint16, target uint16) searchResult {
return binarySearchPastWithBounds(array, target, 0, len(array)-1)
}
// binarySearchPastWithBounds looks for the smallest value larger than or equal to `target`
// If `target` is out of bounds a `searchResult` indicating out of bounds is returned
// `target` does not have to exist in the slice.
//
// Example:
// Suppose the slice is [...10,13...] with `target` equal to 11
// The searchResult will have searchResult.value = 13
func binarySearchPastWithBounds(array []uint16, target uint16, lowIndex int, maxIndex int) searchResult {
highIndex := maxIndex
closestIndex := -1
if target < array[lowIndex] {
return searchResult{0, closestIndex, false}
}
if target > array[maxIndex] {
return searchResult{0, len(array), false}
}
for lowIndex <= highIndex {
middleIndex := (lowIndex + highIndex) / 2
middleValue := array[middleIndex]
if middleValue == target {
return searchResult{middleValue, middleIndex, true}
}
if target < middleValue {
if middleIndex > 0 && target > array[middleIndex-1] {
return searchResult{array[middleIndex], middleIndex, false}
}
highIndex = middleIndex
} else {
if middleIndex < maxIndex && target < array[middleIndex+1] {
return searchResult{array[middleIndex+1], middleIndex + 1, false}
}
lowIndex = middleIndex + 1
}
}
return searchResult{array[closestIndex], closestIndex, false}
}

View File

@@ -52,6 +52,7 @@ func fill(arr []uint64, val uint64) {
arr[i] = val
}
}
func fillRange(arr []uint64, start, end int, val uint64) {
for i := start; i < end; i++ {
arr[i] = val
@@ -112,10 +113,19 @@ func fillArrayXOR(container []uint16, bitmap1, bitmap2 []uint64) {
func highbits(x uint32) uint16 {
return uint16(x >> 16)
}
func lowbits(x uint32) uint16 {
return uint16(x & maxLowBit)
}
func combineLoHi16(lob uint16, hob uint16) uint32 {
return combineLoHi32(uint32(lob), uint32(hob))
}
func combineLoHi32(lob uint32, hob uint32) uint32 {
return uint32(lob) | (hob << 16)
}
const maxLowBit = 0xFFFF
func flipBitmapRange(bitmap []uint64, start int, end int) {
@@ -146,7 +156,6 @@ func resetBitmapRange(bitmap []uint64, start int, end int) {
bitmap[i] = 0
}
bitmap[endword] &= ^(^uint64(0) >> (uint(-end) % 64))
}
func setBitmapRange(bitmap []uint64, start int, end int) {
@@ -242,7 +251,6 @@ func selectBitPosition(w uint64, j int) int {
}
}
return seen + int(counter)
}
func panicOn(err error) {

View File

@@ -12,7 +12,7 @@ This library is part of the [awesome go collection](https://github.com/avelino/a
* [beego](https://github.com/beego/beego)
* [CubeFS](https://github.com/cubefs/cubefs)
* [Amazon EKS Distro](https://github.com/aws/eks-distro)
* [sourcegraph](https://github.com/sourcegraph/sourcegraph)
* [sourcegraph](https://github.com/sourcegraph/sourcegraph-public-snapshot)
* [torrent](https://github.com/anacrolix/torrent)
@@ -25,7 +25,7 @@ It provides methods for setting, clearing, flipping, and testing individual inte
But it also provides set intersection, union, difference, complement, and symmetric operations, as well as tests to check whether any, all, or no bits are set, and querying a bitset's current length and number of positive bits.
BitSets are expanded to the size of the largest set bit; the memory allocation is approximately Max bits, where Max is the largest set bit. BitSets are never shrunk. On creation, a hint can be given for the number of bits that will be used.
BitSets are expanded to the size of the largest set bit; the memory allocation is approximately Max bits, where Max is the largest set bit. BitSets are never shrunk automatically, but `Shrink` and `Compact` methods are available. On creation, a hint can be given for the number of bits that will be used.
Many of the methods, including Set, Clear, and Flip, return a BitSet pointer, which allows for chaining.
@@ -69,6 +69,13 @@ func main() {
}
```
If you have Go 1.23 or better, you can iterate over the set bits like so:
```go
for i := range b.EachSet() {}
```
Package documentation is at: https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc
@@ -125,13 +132,20 @@ E.g.,
## Memory Usage
The memory usage of a bitset using `N` bits is at least `N/8` bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](http://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring).
The memory usage of a bitset using `N` bits is at least `N/8` bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](https://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring).
## Implementation Note
The `roaring` library allows you to go back and forth between compressed Roaring bitmaps and the conventional bitset instances:
```Go
mybitset := roaringbitmap.ToBitSet()
newroaringbitmap := roaring.FromBitSet(mybitset)
```
Go 1.9 introduced a native `math/bits` library. We provide backward compatibility to Go 1.7, which might be removed.
It is possible that a later version will match the `math/bits` return signature for counts (which is `int`, rather than our library's `uint64`). If so, the version will be bumped.
### Goroutine safety
In general, it's not safe to access the same BitSet using different goroutines--they are unsynchronized for performance.
Should you want to access a BitSet from more than one goroutine, you should provide synchronization. Typically this is done by using channels to pass the *BitSet around (in Go style; so there is only ever one owner), or by using `sync.Mutex` to serialize operations on BitSets.
## Installation

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
//go:build go1.23
// +build go1.23
package bitset
import (
"iter"
"math/bits"
)
func (b *BitSet) EachSet() iter.Seq[uint] {
return func(yield func(uint) bool) {
for wordIndex, word := range b.set {
idx := 0
for trail := bits.TrailingZeros64(word); trail != 64; trail = bits.TrailingZeros64(word >> idx) {
if !yield(uint(wordIndex<<log2WordSize + idx + trail)) {
return
}
idx += trail + 1
}
}
}
}

8866
vendor/github.com/bits-and-blooms/bitset/pext.gen.go generated vendored Normal file
View File

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More