-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,013 additions
and
1,012 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,118 +1,118 @@ | ||
package util | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"strings" | ||
"fmt" | ||
"math" | ||
"strings" | ||
) | ||
|
||
// Allows using named bitmask values. Useful for commandline flag options | ||
// that need to be treated as bitmask values. In all functions, names are | ||
// case-insensitive. | ||
type BitMask struct { | ||
fields map[string]uint64 | ||
values map[uint64]string | ||
nextVal uint64 | ||
fields map[string]uint64 | ||
values map[uint64]string | ||
nextVal uint64 | ||
} | ||
|
||
func (this *BitMask) addName(name string) error { | ||
if this.nextVal == 0 { | ||
this.fields = make(map[string]uint64, 64) | ||
this.values = make(map[uint64]string, 64) | ||
this.nextVal = 1 | ||
} | ||
name = strings.ToLower(name) | ||
if name == "all" || name == "none" { | ||
return fmt.Errorf("reserved field name: %s", name) | ||
} | ||
_, ok := this.fields[name] | ||
if ok { | ||
return fmt.Errorf("field already exists: %s", name) | ||
} | ||
if this.nextVal == math.MaxUint64 { | ||
return fmt.Errorf("too many fields") | ||
} | ||
this.fields[name] = this.nextVal | ||
this.values[this.nextVal] = name | ||
if this.nextVal == uint64(0x8000000000000000) { | ||
this.nextVal = math.MaxUint64 | ||
} else { | ||
this.nextVal <<= 1 | ||
} | ||
return nil | ||
if this.nextVal == 0 { | ||
this.fields = make(map[string]uint64, 64) | ||
this.values = make(map[uint64]string, 64) | ||
this.nextVal = 1 | ||
} | ||
name = strings.ToLower(name) | ||
if name == "all" || name == "none" { | ||
return fmt.Errorf("reserved field name: %s", name) | ||
} | ||
_, ok := this.fields[name] | ||
if ok { | ||
return fmt.Errorf("field already exists: %s", name) | ||
} | ||
if this.nextVal == math.MaxUint64 { | ||
return fmt.Errorf("too many fields") | ||
} | ||
this.fields[name] = this.nextVal | ||
this.values[this.nextVal] = name | ||
if this.nextVal == uint64(0x8000000000000000) { | ||
this.nextVal = math.MaxUint64 | ||
} else { | ||
this.nextVal <<= 1 | ||
} | ||
return nil | ||
} | ||
|
||
// Add list of names to the bitmask. Takes a single string with names | ||
// separated by the '|' character. | ||
func (this *BitMask) Add(names string) error { | ||
ss := strings.Split(names, "|") | ||
if len(ss) == 0 { | ||
return fmt.Errorf("invalid bitmask string") | ||
} | ||
for _, sn := range ss { | ||
err := this.addName(sn) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
ss := strings.Split(names, "|") | ||
if len(ss) == 0 { | ||
return fmt.Errorf("invalid bitmask string") | ||
} | ||
for _, sn := range ss { | ||
err := this.addName(sn) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// Get the bitmask value for a list of names. Takes a single string with names | ||
// separated by the '|' character. | ||
func (this *BitMask) Parse(s string) (uint64, error) { | ||
s = strings.ToLower(s) | ||
if s == "all" { | ||
v := uint64(0) | ||
for vn := range this.values { | ||
v |= vn | ||
} | ||
return v, nil | ||
} else if s == "none" { | ||
return 0, nil | ||
} | ||
ss := strings.Split(s, "|") | ||
if len(ss) == 0 { | ||
return 0, fmt.Errorf("invalid bitmask string") | ||
} | ||
v := uint64(0) | ||
for _, sn := range ss { | ||
vn, ok := this.fields[sn] | ||
if !ok { | ||
return 0, fmt.Errorf("field not found: %s", sn) | ||
} | ||
v |= vn | ||
} | ||
return v, nil | ||
s = strings.ToLower(s) | ||
if s == "all" { | ||
v := uint64(0) | ||
for vn := range this.values { | ||
v |= vn | ||
} | ||
return v, nil | ||
} else if s == "none" { | ||
return 0, nil | ||
} | ||
ss := strings.Split(s, "|") | ||
if len(ss) == 0 { | ||
return 0, fmt.Errorf("invalid bitmask string") | ||
} | ||
v := uint64(0) | ||
for _, sn := range ss { | ||
vn, ok := this.fields[sn] | ||
if !ok { | ||
return 0, fmt.Errorf("field not found: %s", sn) | ||
} | ||
v |= vn | ||
} | ||
return v, nil | ||
} | ||
|
||
// Given a bitmask value, check if the value corresponding to the given | ||
// string is set. Takes a single string with names separated by the '|' | ||
// character. | ||
func (this *BitMask) IsSet(v uint64, s string) bool { | ||
check, err := this.Parse(s) | ||
if err == nil && (v & check) != 0 { | ||
return true | ||
} | ||
return false | ||
check, err := this.Parse(s) | ||
if err == nil && (v&check) != 0 { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
// Get the names for a bitmask value. | ||
func (this *BitMask) Format(v uint64) (string, error) { | ||
if v == 0 { | ||
return "none", nil | ||
} | ||
s := make([]string, 0) | ||
vn := uint64(1) | ||
for vn != uint64(0x8000000000000000) { | ||
if (v & vn) != 0 { | ||
sn, ok := this.values[vn] | ||
if !ok { | ||
return "", fmt.Errorf("bitmask not found: %d\n", vn) | ||
} | ||
s = append(s, sn) | ||
} | ||
vn <<= 1 | ||
} | ||
return strings.Join(s, "|"), nil | ||
if v == 0 { | ||
return "none", nil | ||
} | ||
s := make([]string, 0) | ||
vn := uint64(1) | ||
for vn != uint64(0x8000000000000000) { | ||
if (v & vn) != 0 { | ||
sn, ok := this.values[vn] | ||
if !ok { | ||
return "", fmt.Errorf("bitmask not found: %d\n", vn) | ||
} | ||
s = append(s, sn) | ||
} | ||
vn <<= 1 | ||
} | ||
return strings.Join(s, "|"), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,31 @@ | ||
package util | ||
|
||
import ( | ||
"errors" | ||
"os/exec" | ||
"time" | ||
"errors" | ||
"os/exec" | ||
"time" | ||
) | ||
|
||
// ErrReadTimeout is the error used when a command times out before completing. | ||
var ErrCommandTimeout = errors.New("command timed out") | ||
|
||
// Run a command, killing it if it does not finish before the specified timeout | ||
func RunCommandWithTimeout(cmd *exec.Cmd, timeout time.Duration) error { | ||
done := make(chan error, 1) | ||
t := time.After(timeout) | ||
done := make(chan error, 1) | ||
t := time.After(timeout) | ||
|
||
err := cmd.Start() | ||
if err != nil { | ||
return err | ||
} | ||
go func() { | ||
done <- cmd.Wait() | ||
}() | ||
select { | ||
case err := <- done: | ||
return err | ||
case <- t: | ||
cmd.Process.Kill() | ||
return ErrCommandTimeout | ||
} | ||
err := cmd.Start() | ||
if err != nil { | ||
return err | ||
} | ||
go func() { | ||
done <- cmd.Wait() | ||
}() | ||
select { | ||
case err := <-done: | ||
return err | ||
case <-t: | ||
cmd.Process.Kill() | ||
return ErrCommandTimeout | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,53 @@ | ||
package util | ||
|
||
import ( | ||
"sync" | ||
"sync" | ||
) | ||
|
||
// Pool of concurrency slots. Can be used, for example, to limit asynchronous | ||
// processing of items in a queue. Warning: If you mistakenly do more Release() | ||
// calls than Get() calls, the extra Release() call will block, as well as a | ||
// Close() call. | ||
type ConcurrencyPool struct { | ||
pool chan int | ||
closed bool | ||
mutex sync.Mutex | ||
pool chan int | ||
closed bool | ||
mutex sync.Mutex | ||
} | ||
|
||
// Create a new ConcurrencyPool | ||
func NewConcurrencyPool(slots int) *ConcurrencyPool { | ||
slots = MaxInt(1, slots) | ||
cp := new(ConcurrencyPool) | ||
cp.pool = make(chan int, slots) | ||
for i := 0; i < slots; i++ { | ||
cp.pool <- 0 | ||
} | ||
return cp | ||
slots = MaxInt(1, slots) | ||
cp := new(ConcurrencyPool) | ||
cp.pool = make(chan int, slots) | ||
for i := 0; i < slots; i++ { | ||
cp.pool <- 0 | ||
} | ||
return cp | ||
} | ||
|
||
// Get one slot from the ConcurrencyPool | ||
func (this *ConcurrencyPool) Get() { | ||
<- this.pool | ||
<-this.pool | ||
} | ||
|
||
// Release one slot back to the ConcurrencyPool | ||
func (this *ConcurrencyPool) Release() { | ||
this.mutex.Lock() | ||
defer this.mutex.Unlock() | ||
// avoid writing to a closed channel, which would panic | ||
if !this.closed { | ||
this.pool <- 0 | ||
} | ||
this.mutex.Lock() | ||
defer this.mutex.Unlock() | ||
// avoid writing to a closed channel, which would panic | ||
if !this.closed { | ||
this.pool <- 0 | ||
} | ||
} | ||
|
||
// Close the ConcurrencyPool. | ||
// After closing, all Get() will unblock and all future Get() and Release() | ||
// will just return without blocking. | ||
func (this *ConcurrencyPool) Close() { | ||
this.mutex.Lock() | ||
defer this.mutex.Unlock() | ||
if !this.closed { | ||
close(this.pool) | ||
this.closed = true | ||
} | ||
this.mutex.Lock() | ||
defer this.mutex.Unlock() | ||
if !this.closed { | ||
close(this.pool) | ||
this.closed = true | ||
} | ||
} |
Oops, something went wrong.