Skip to content

Commit

Permalink
init: Initialize hashset
Browse files Browse the repository at this point in the history
  • Loading branch information
1eedaegon committed Mar 29, 2024
1 parent f1d335f commit c7b3de1
Show file tree
Hide file tree
Showing 8 changed files with 382 additions and 0 deletions.
87 changes: 87 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
push:
branches: ["main"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["main"]
schedule:
- cron: "28 16 * * 0"

jobs:
analyze:
name: Analyze
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners
# Consider using larger runners for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'go' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'go' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write

strategy:
fail-fast: false
matrix:
language: ["go"]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v3

# Initialize go version for v1.21
- name: Install Go
uses: actions/setup-go@v4
with:
go-version-file: go.mod

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality

# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
25 changes: 25 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'

- name: Test
run: go test -v ./...
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# go-hashset

[![Go](https://pkg.go.dev/badge/github.com/1eedaegon/go-hashset.svg)](https://pkg.go.dev/github.com/1eedaegon/go-hashset)
[![CI](https://github.com/1eedaegon/go-hashset/actions/workflows/go.yml/badge.svg)](https://github.com/1eedaegon/go-hashset/actions/workflows/go.yml)
[![CodeQL](https://github.com/1eedaegon/go-hashset/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/1eedaegon/go-hashset/actions/workflows/codeql.yml)

A go library hashset for O(1)

## Example

```go
import (
...
port "github.com/1eedaegon/go-hashset"
...
)

ports := port.Get(3)
// ports is something like []int{10000, 10001, 10002}
```

Or

```go
import (
...
port "github.com/1eedaegon/go-hashset"
...
)

ports := port.GetS(3)
// ports is something like []string{"10000", "10001", "10002"}
```

## License

[MIT](LICENSE)
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/1eedaegon/go-hashset

go 1.21.0

require github.com/stretchr/testify v1.9.0

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
126 changes: 126 additions & 0 deletions hashset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package hashset

import "sync"

// Set represents a thread-safe collection of unique elements.
type Set struct {
mu sync.RWMutex // Guards access to the internal hash map.
hash map[interface{}]bool // Stores elements as keys with a boolean value.
}

// New initializes and returns a new Set with optional initial elements.
func New(initial ...interface{}) *Set {
s := &Set{
hash: make(map[interface{}]bool),
}
for _, v := range initial {
s.Add(v)
}
return s
}

// Add inserts an element into the Set. If the element is already present, it does nothing.
func (s *Set) Add(element interface{}) {
s.mu.Lock()
defer s.mu.Unlock()
s.hash[element] = true
}

// Remove deletes an element from the Set. If the element is not present, it does nothing.
func (s *Set) Remove(element interface{}) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.hash, element)
}

// Contains checks if an element is present in the Set.
func (s *Set) Contains(element interface{}) bool {
s.mu.RLock()
defer s.mu.RUnlock()
_, exists := s.hash[element]
return exists
}

// Difference returns a new Set containing elements present in the original Set but not in the given Set.
func (s *Set) Difference(set *Set) *Set {
diff := make(map[interface{}]bool)
s.mu.RLock()
defer s.mu.RUnlock()
for k := range s.hash {
if _, exists := set.hash[k]; !exists {
diff[k] = true
}
}
return &Set{hash: diff}
}

// Do applies a given function to each element of the Set. This can be used for iterating over the Set.
func (s *Set) Do(f func(interface{})) {
s.mu.RLock()
defer s.mu.RUnlock()
for k := range s.hash {
f(k)
}
}

// Intersection returns a new Set containing elements that are present in both Sets.
func (s *Set) Intersection(set *Set) *Set {
intersect := make(map[interface{}]bool)
s.mu.RLock()
defer s.mu.RUnlock()
for k := range s.hash {
if _, exists := set.hash[k]; exists {
intersect[k] = true
}
}
return &Set{hash: intersect}
}

// Len returns the number of elements in the Set.
func (s *Set) Len() int {
s.mu.RLock()
defer s.mu.RUnlock()
return len(s.hash)
}

// SubsetOf checks if the Set is a subset of the given Set.
func (s *Set) SubsetOf(set *Set) bool {
s.mu.RLock()
defer s.mu.RUnlock()
if s.Len() > set.Len() {
return false
}
for k := range s.hash {
if _, exists := set.hash[k]; !exists {
return false
}
}
return true
}

// Union returns a new Set containing all elements that are present in either Set.
func (s *Set) Union(set *Set) *Set {
union := make(map[interface{}]bool)
s.mu.Lock()
for k := range s.hash {
union[k] = true
}
s.mu.Unlock()

s.mu.Lock()
for k := range set.hash {
union[k] = true
}
s.mu.Unlock()
return &Set{hash: union}
}

func (s *Set) ToSlice() []interface{} {
uniTypeSlice := make([]interface{}, 0)
s.mu.RLock()
for key := range s.hash {
uniTypeSlice = append(uniTypeSlice, key)
}
s.mu.RUnlock()
return uniTypeSlice
}
78 changes: 78 additions & 0 deletions hashset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package hashset

import (
"strconv"
"sync"
"testing"

"github.com/stretchr/testify/require"
)

/*
0. 기본 연산
1. 추가
2. 삭제
3. 요소 확인
1. 값의 중복이 없어야한다.
2. slice가오면 set으로 변경되어야한다.
3. set을 slice로 변경할 수 있어야한다.
4. set(집합) 연산이 가능해야한다.
1. intersection
2. union
3. difference
5. 거의 모든타입 지원이 되어야한다.
1. 함수타입은 별도의 uuid와 포인터로 보관되어야한다.
2. 구조체타입도 uuid로 할까 마샬링으로 할까 했는데 uuid가 좋아보이넹
6. 같은 set에 대해 동시작업이 원자성을 보장받아야한다.
*/

// func testGongurrency(numOfGoroutine, numOfEashWork int, work func(prefix string, num int)) {
// var wg sync.WaitGroup
// for nthGoroutine := 0; nthGoroutine < numOfGoroutine; nthGoroutine++ {
// wg.Add(1)
// go func(n int) {
// defer wg.Done()
// for nthWork := 0; nthWork < numOfEashWork; nthWork++ {
// work("testing-", nthWork)
// }

// }(nthGoroutine)
// }
// wg.Wait()
// }
func TestConcurrentAddElement(t *testing.T) {
s := New()
var wg sync.WaitGroup
for nthGoroutine := 0; nthGoroutine < 10; nthGoroutine++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
for nthWork := 0; nthWork < 100; nthWork++ {
s.Add(strconv.Itoa(n) + ".testing-" + strconv.Itoa(nthWork))
}

}(nthGoroutine)
}
wg.Wait()
require.Equal(t, 1000, s.Len())

// testGongurrency(100, 10, func(prefix string, num int) {
// set.Remove("prefix" + strconv.Itoa(num))
// })
// require.Equal(t, set.Len(), 0)

}

// func TestConcurrentRemoveElement(t *testing.T) {}
// func TestMembershipCheck(t *testing.T) {}
// func TestDuplicate(t *testing.T) {}
// func TestConvertToSet(t *testing.T) {}
// func TestConvertToSlice(t *testing.T) {}
// func TestUnion(t *testing.T) {}
// func TestIntersection(t *testing.T) {}
// func TestDifference(t *testing.T) {}
// func TestFunctionElement(t *testing.T) {}
// func TestStructElement(t *testing.T) {}
8 changes: 8 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.PHONY: install
install:
go mod tidy

.PHONY: test
test: install
go test ./... -race

0 comments on commit c7b3de1

Please sign in to comment.