mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
Also made the add/remove/getAsStringSlice receiver for this type. Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
191 lines
4.6 KiB
Go
191 lines
4.6 KiB
Go
// Copyright 2012-2019 The NATS Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package server
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"net/url"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// This map is used to store URLs string as the key with a reference count as
|
|
// the value. This is used to handle gossiped URLs such as connect_urls, etc..
|
|
type refCountedUrlSet map[string]int
|
|
|
|
// Ascii numbers 0-9
|
|
const (
|
|
asciiZero = 48
|
|
asciiNine = 57
|
|
)
|
|
|
|
// parseSize expects decimal positive numbers. We
|
|
// return -1 to signal error.
|
|
func parseSize(d []byte) (n int) {
|
|
const maxParseSizeLen = 9 //999M
|
|
|
|
l := len(d)
|
|
if l == 0 || l > maxParseSizeLen {
|
|
return -1
|
|
}
|
|
var (
|
|
i int
|
|
dec byte
|
|
)
|
|
|
|
// Note: Use `goto` here to avoid for loop in order
|
|
// to have the function be inlined.
|
|
// See: https://github.com/golang/go/issues/14768
|
|
loop:
|
|
dec = d[i]
|
|
if dec < asciiZero || dec > asciiNine {
|
|
return -1
|
|
}
|
|
n = n*10 + (int(dec) - asciiZero)
|
|
|
|
i++
|
|
if i < l {
|
|
goto loop
|
|
}
|
|
return n
|
|
}
|
|
|
|
// parseInt64 expects decimal positive numbers. We
|
|
// return -1 to signal error
|
|
func parseInt64(d []byte) (n int64) {
|
|
if len(d) == 0 {
|
|
return -1
|
|
}
|
|
for _, dec := range d {
|
|
if dec < asciiZero || dec > asciiNine {
|
|
return -1
|
|
}
|
|
n = n*10 + (int64(dec) - asciiZero)
|
|
}
|
|
return n
|
|
}
|
|
|
|
// Helper to move from float seconds to time.Duration
|
|
func secondsToDuration(seconds float64) time.Duration {
|
|
ttl := seconds * float64(time.Second)
|
|
return time.Duration(ttl)
|
|
}
|
|
|
|
// Parse a host/port string with a default port to use
|
|
// if none (or 0 or -1) is specified in `hostPort` string.
|
|
func parseHostPort(hostPort string, defaultPort int) (host string, port int, err error) {
|
|
if hostPort != "" {
|
|
host, sPort, err := net.SplitHostPort(hostPort)
|
|
if ae, ok := err.(*net.AddrError); ok && strings.Contains(ae.Err, "missing port") {
|
|
// try appending the current port
|
|
host, sPort, err = net.SplitHostPort(fmt.Sprintf("%s:%d", hostPort, defaultPort))
|
|
}
|
|
if err != nil {
|
|
return "", -1, err
|
|
}
|
|
port, err = strconv.Atoi(strings.TrimSpace(sPort))
|
|
if err != nil {
|
|
return "", -1, err
|
|
}
|
|
if port == 0 || port == -1 {
|
|
port = defaultPort
|
|
}
|
|
return strings.TrimSpace(host), port, nil
|
|
}
|
|
return "", -1, errors.New("no hostport specified")
|
|
}
|
|
|
|
// Returns true if URL u1 represents the same URL than u2,
|
|
// false otherwise.
|
|
func urlsAreEqual(u1, u2 *url.URL) bool {
|
|
return reflect.DeepEqual(u1, u2)
|
|
}
|
|
|
|
// comma produces a string form of the given number in base 10 with
|
|
// commas after every three orders of magnitude.
|
|
//
|
|
// e.g. comma(834142) -> 834,142
|
|
//
|
|
// This function was copied from the github.com/dustin/go-humanize
|
|
// package and is Copyright Dustin Sallings <dustin@spy.net>
|
|
func comma(v int64) string {
|
|
sign := ""
|
|
|
|
// Min int64 can't be negated to a usable value, so it has to be special cased.
|
|
if v == math.MinInt64 {
|
|
return "-9,223,372,036,854,775,808"
|
|
}
|
|
|
|
if v < 0 {
|
|
sign = "-"
|
|
v = 0 - v
|
|
}
|
|
|
|
parts := []string{"", "", "", "", "", "", ""}
|
|
j := len(parts) - 1
|
|
|
|
for v > 999 {
|
|
parts[j] = strconv.FormatInt(v%1000, 10)
|
|
switch len(parts[j]) {
|
|
case 2:
|
|
parts[j] = "0" + parts[j]
|
|
case 1:
|
|
parts[j] = "00" + parts[j]
|
|
}
|
|
v = v / 1000
|
|
j--
|
|
}
|
|
parts[j] = strconv.Itoa(int(v))
|
|
return sign + strings.Join(parts[j:], ",")
|
|
}
|
|
|
|
// Adds urlStr to the given map. If the string was already present, simply
|
|
// bumps the reference count.
|
|
// Returns true only if it was added for the first time.
|
|
func (m refCountedUrlSet) addUrl(urlStr string) bool {
|
|
m[urlStr]++
|
|
return m[urlStr] == 1
|
|
}
|
|
|
|
// Removes urlStr from the given map. If the string is not present, nothing
|
|
// is done and false is returned.
|
|
// If the string was present, its reference count is decreased. Returns true
|
|
// if this was the last reference, false otherwise.
|
|
func (m refCountedUrlSet) removeUrl(urlStr string) bool {
|
|
removed := false
|
|
if ref, ok := m[urlStr]; ok {
|
|
if ref == 1 {
|
|
removed = true
|
|
delete(m, urlStr)
|
|
} else {
|
|
m[urlStr]--
|
|
}
|
|
}
|
|
return removed
|
|
}
|
|
|
|
// Returns the unique URLs in this map as a slice
|
|
func (m refCountedUrlSet) getAsStringSlice() []string {
|
|
a := make([]string, 0, len(m))
|
|
for u := range m {
|
|
a = append(a, u)
|
|
}
|
|
return a
|
|
}
|