1
0
mirror of https://github.com/gogf/gf.git synced 2025-04-05 11:18:50 +08:00

项目目录调整,开源协议修改 GPLv3 -> MIT

This commit is contained in:
John 2017-11-22 16:02:50 +08:00
parent c075c395f8
commit d2bc721fb9
94 changed files with 10878 additions and 373 deletions

394
LICENSE
View File

@ -1,373 +1,21 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
MIT License
Copyright (c) 2017 john@johng.cn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

View File

@ -0,0 +1,5 @@
package gmvc

3
g/core/gmvc/model.go Normal file
View File

@ -0,0 +1,3 @@
package gmvc

3
g/core/gmvc/view.go Normal file
View File

@ -0,0 +1,3 @@
package gmvc

View File

@ -0,0 +1,76 @@
// Copyright 2014 Google Inc.
//
// 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.
// +build ignore
// This binary compares memory usage between btree and gollrb.
package gbtree
import (
"flag"
"fmt"
"math/rand"
"runtime"
"time"
"github.com/google/btree"
"github.com/petar/GoLLRB/llrb"
)
var (
size = flag.Int("size", 1000000, "size of the tree to build")
degree = flag.Int("degree", 8, "degree of btree")
gollrb = flag.Bool("llrb", false, "use llrb instead of btree")
)
func main() {
flag.Parse()
vals := rand.Perm(*size)
var t, v interface{}
v = vals
var stats runtime.MemStats
for i := 0; i < 10; i++ {
runtime.GC()
}
fmt.Println("-------- BEFORE ----------")
runtime.ReadMemStats(&stats)
fmt.Printf("%+v\n", stats)
start := time.Now()
if *gollrb {
tr := llrb.New()
for _, v := range vals {
tr.ReplaceOrInsert(llrb.Int(v))
}
t = tr // keep it around
} else {
tr := btree.New(*degree)
for _, v := range vals {
tr.ReplaceOrInsert(btree.Int(v))
}
t = tr // keep it around
}
fmt.Printf("%v inserts in %v\n", *size, time.Since(start))
fmt.Println("-------- AFTER ----------")
runtime.ReadMemStats(&stats)
fmt.Printf("%+v\n", stats)
for i := 0; i < 10; i++ {
runtime.GC()
}
fmt.Println("-------- AFTER GC ----------")
runtime.ReadMemStats(&stats)
fmt.Printf("%+v\n", stats)
if t == v {
fmt.Println("to make sure vals and tree aren't GC'd")
}
}

View File

@ -0,0 +1,776 @@
// B+树来源于from https://github.com/google/btree
package gbtree
import (
"fmt"
"io"
"sort"
"strings"
"sync"
)
// Item represents a single object in the tree.
type Item interface {
// Less tests whether the current item is less than the given argument.
//
// This must provide a strict weak ordering.
// If !a.Less(b) && !b.Less(a), we treat this to mean a == b (i.e. we can only
// hold one of either a or b in the tree).
Less(than Item) bool
}
const (
DefaultFreeListSize = 32
)
var (
nilItems = make(items, 16)
nilChildren = make(children, 16)
)
// FreeList represents a free list of btree nodes. By default each
// BTree has its own FreeList, but multiple BTrees can share the same
// FreeList.
// Two Btrees using the same freelist are safe for concurrent write access.
type FreeList struct {
mu sync.Mutex
freelist []*node
}
// NewFreeList creates a new free list.
// size is the maximum size of the returned free list.
func NewFreeList(size int) *FreeList {
return &FreeList{freelist: make([]*node, 0, size)}
}
func (f *FreeList) newNode() (n *node) {
f.mu.Lock()
index := len(f.freelist) - 1
if index < 0 {
f.mu.Unlock()
return new(node)
}
n = f.freelist[index]
f.freelist[index] = nil
f.freelist = f.freelist[:index]
f.mu.Unlock()
return
}
func (f *FreeList) freeNode(n *node) {
f.mu.Lock()
if len(f.freelist) < cap(f.freelist) {
f.freelist = append(f.freelist, n)
}
f.mu.Unlock()
}
// ItemIterator allows callers of Ascend* to iterate in-order over portions of
// the tree. When this function returns false, iteration will stop and the
// associated Ascend* function will immediately return.
type ItemIterator func(i Item) bool
// New creates a new B-Tree with the given degree.
//
// New(2), for example, will create a 2-3-4 tree (each node contains 1-3 items
// and 2-4 children).
func New(degree int) *BTree {
return NewWithFreeList(degree, NewFreeList(DefaultFreeListSize))
}
// NewWithFreeList creates a new B-Tree that uses the given node free list.
func NewWithFreeList(degree int, f *FreeList) *BTree {
if degree <= 1 {
panic("bad degree")
}
return &BTree{
degree: degree,
cow: &copyOnWriteContext{freelist: f},
}
}
// items stores items in a node.
type items []Item
// insertAt inserts a value into the given index, pushing all subsequent values
// forward.
func (s *items) insertAt(index int, item Item) {
*s = append(*s, nil)
if index < len(*s) {
copy((*s)[index+1:], (*s)[index:])
}
(*s)[index] = item
}
// removeAt removes a value at a given index, pulling all subsequent values
// back.
func (s *items) removeAt(index int) Item {
item := (*s)[index]
copy((*s)[index:], (*s)[index+1:])
(*s)[len(*s)-1] = nil
*s = (*s)[:len(*s)-1]
return item
}
// pop removes and returns the last element in the list.
func (s *items) pop() (out Item) {
index := len(*s) - 1
out = (*s)[index]
(*s)[index] = nil
*s = (*s)[:index]
return
}
// truncate truncates this instance at index so that it contains only the
// first index items. index must be less than or equal to length.
func (s *items) truncate(index int) {
var toClear items
*s, toClear = (*s)[:index], (*s)[index:]
for len(toClear) > 0 {
toClear = toClear[copy(toClear, nilItems):]
}
}
// find returns the index where the given item should be inserted into this
// list. 'found' is true if the item already exists in the list at the given
// index.
func (s items) find(item Item) (index int, found bool) {
i := sort.Search(len(s), func(i int) bool {
return item.Less(s[i])
})
if i > 0 && !s[i-1].Less(item) {
return i - 1, true
}
return i, false
}
// children stores child nodes in a node.
type children []*node
// insertAt inserts a value into the given index, pushing all subsequent values
// forward.
func (s *children) insertAt(index int, n *node) {
*s = append(*s, nil)
if index < len(*s) {
copy((*s)[index+1:], (*s)[index:])
}
(*s)[index] = n
}
// removeAt removes a value at a given index, pulling all subsequent values
// back.
func (s *children) removeAt(index int) *node {
n := (*s)[index]
copy((*s)[index:], (*s)[index+1:])
(*s)[len(*s)-1] = nil
*s = (*s)[:len(*s)-1]
return n
}
// pop removes and returns the last element in the list.
func (s *children) pop() (out *node) {
index := len(*s) - 1
out = (*s)[index]
(*s)[index] = nil
*s = (*s)[:index]
return
}
// truncate truncates this instance at index so that it contains only the
// first index children. index must be less than or equal to length.
func (s *children) truncate(index int) {
var toClear children
*s, toClear = (*s)[:index], (*s)[index:]
for len(toClear) > 0 {
toClear = toClear[copy(toClear, nilChildren):]
}
}
// node is an internal node in a tree.
//
// It must at all times maintain the invariant that either
// * len(children) == 0, len(items) unconstrained
// * len(children) == len(items) + 1
type node struct {
items items
children children
cow *copyOnWriteContext
}
func (n *node) mutableFor(cow *copyOnWriteContext) *node {
if n.cow == cow {
return n
}
out := cow.newNode()
if cap(out.items) >= len(n.items) {
out.items = out.items[:len(n.items)]
} else {
out.items = make(items, len(n.items), cap(n.items))
}
copy(out.items, n.items)
// Copy children
if cap(out.children) >= len(n.children) {
out.children = out.children[:len(n.children)]
} else {
out.children = make(children, len(n.children), cap(n.children))
}
copy(out.children, n.children)
return out
}
func (n *node) mutableChild(i int) *node {
c := n.children[i].mutableFor(n.cow)
n.children[i] = c
return c
}
// split splits the given node at the given index. The current node shrinks,
// and this function returns the item that existed at that index and a new node
// containing all items/children after it.
func (n *node) split(i int) (Item, *node) {
item := n.items[i]
next := n.cow.newNode()
next.items = append(next.items, n.items[i+1:]...)
n.items.truncate(i)
if len(n.children) > 0 {
next.children = append(next.children, n.children[i+1:]...)
n.children.truncate(i + 1)
}
return item, next
}
// maybeSplitChild checks if a child should be split, and if so splits it.
// Returns whether or not a split occurred.
func (n *node) maybeSplitChild(i, maxItems int) bool {
if len(n.children[i].items) < maxItems {
return false
}
first := n.mutableChild(i)
item, second := first.split(maxItems / 2)
n.items.insertAt(i, item)
n.children.insertAt(i+1, second)
return true
}
// insert inserts an item into the subtree rooted at this node, making sure
// no nodes in the subtree exceed maxItems items. Should an equivalent item be
// be found/replaced by insert, it will be returned.
func (n *node) insert(item Item, maxItems int) Item {
i, found := n.items.find(item)
if found {
out := n.items[i]
n.items[i] = item
return out
}
if len(n.children) == 0 {
n.items.insertAt(i, item)
return nil
}
if n.maybeSplitChild(i, maxItems) {
inTree := n.items[i]
switch {
case item.Less(inTree):
// no change, we want first split node
case inTree.Less(item):
i++ // we want second split node
default:
out := n.items[i]
n.items[i] = item
return out
}
}
return n.mutableChild(i).insert(item, maxItems)
}
// get finds the given key in the subtree and returns it.
func (n *node) get(key Item) Item {
i, found := n.items.find(key)
if found {
return n.items[i]
} else if len(n.children) > 0 {
return n.children[i].get(key)
}
return nil
}
// min returns the first item in the subtree.
func min(n *node) Item {
if n == nil {
return nil
}
for len(n.children) > 0 {
n = n.children[0]
}
if len(n.items) == 0 {
return nil
}
return n.items[0]
}
// max returns the last item in the subtree.
func max(n *node) Item {
if n == nil {
return nil
}
for len(n.children) > 0 {
n = n.children[len(n.children)-1]
}
if len(n.items) == 0 {
return nil
}
return n.items[len(n.items)-1]
}
// toRemove details what item to remove in a node.remove call.
type toRemove int
const (
removeItem toRemove = iota // removes the given item
removeMin // removes smallest item in the subtree
removeMax // removes largest item in the subtree
)
// remove removes an item from the subtree rooted at this node.
func (n *node) remove(item Item, minItems int, typ toRemove) Item {
var i int
var found bool
switch typ {
case removeMax:
if len(n.children) == 0 {
return n.items.pop()
}
i = len(n.items)
case removeMin:
if len(n.children) == 0 {
return n.items.removeAt(0)
}
i = 0
case removeItem:
i, found = n.items.find(item)
if len(n.children) == 0 {
if found {
return n.items.removeAt(i)
}
return nil
}
default:
panic("invalid type")
}
// If we get to here, we have children.
if len(n.children[i].items) <= minItems {
return n.growChildAndRemove(i, item, minItems, typ)
}
child := n.mutableChild(i)
// Either we had enough items to begin with, or we've done some
// merging/stealing, because we've got enough now and we're ready to return
// stuff.
if found {
// The item exists at index 'i', and the child we've selected can give us a
// predecessor, since if we've gotten here it's got > minItems items in it.
out := n.items[i]
// We use our special-case 'remove' call with typ=maxItem to pull the
// predecessor of item i (the rightmost leaf of our immediate left child)
// and set it into where we pulled the item from.
n.items[i] = child.remove(nil, minItems, removeMax)
return out
}
// Final recursive call. Once we're here, we know that the item isn't in this
// node and that the child is big enough to remove from.
return child.remove(item, minItems, typ)
}
// growChildAndRemove grows child 'i' to make sure it's possible to remove an
// item from it while keeping it at minItems, then calls remove to actually
// remove it.
//
// Most documentation says we have to do two sets of special casing:
// 1) item is in this node
// 2) item is in child
// In both cases, we need to handle the two subcases:
// A) node has enough values that it can spare one
// B) node doesn't have enough values
// For the latter, we have to check:
// a) left sibling has node to spare
// b) right sibling has node to spare
// c) we must merge
// To simplify our code here, we handle cases #1 and #2 the same:
// If a node doesn't have enough items, we make sure it does (using a,b,c).
// We then simply redo our remove call, and the second time (regardless of
// whether we're in case 1 or 2), we'll have enough items and can guarantee
// that we hit case A.
func (n *node) growChildAndRemove(i int, item Item, minItems int, typ toRemove) Item {
if i > 0 && len(n.children[i-1].items) > minItems {
// Steal from left child
child := n.mutableChild(i)
stealFrom := n.mutableChild(i - 1)
stolenItem := stealFrom.items.pop()
child.items.insertAt(0, n.items[i-1])
n.items[i-1] = stolenItem
if len(stealFrom.children) > 0 {
child.children.insertAt(0, stealFrom.children.pop())
}
} else if i < len(n.items) && len(n.children[i+1].items) > minItems {
// steal from right child
child := n.mutableChild(i)
stealFrom := n.mutableChild(i + 1)
stolenItem := stealFrom.items.removeAt(0)
child.items = append(child.items, n.items[i])
n.items[i] = stolenItem
if len(stealFrom.children) > 0 {
child.children = append(child.children, stealFrom.children.removeAt(0))
}
} else {
if i >= len(n.items) {
i--
}
child := n.mutableChild(i)
// merge with right child
mergeItem := n.items.removeAt(i)
mergeChild := n.children.removeAt(i + 1)
child.items = append(child.items, mergeItem)
child.items = append(child.items, mergeChild.items...)
child.children = append(child.children, mergeChild.children...)
n.cow.freeNode(mergeChild)
}
return n.remove(item, minItems, typ)
}
type direction int
const (
descend = direction(-1)
ascend = direction(+1)
)
// iterate provides a simple method for iterating over elements in the tree.
//
// When ascending, the 'start' should be less than 'stop' and when descending,
// the 'start' should be greater than 'stop'. Setting 'includeStart' to true
// will force the iterator to include the first item when it equals 'start',
// thus creating a "greaterOrEqual" or "lessThanEqual" rather than just a
// "greaterThan" or "lessThan" queries.
func (n *node) iterate(dir direction, start, stop Item, includeStart bool, hit bool, iter ItemIterator) (bool, bool) {
var ok bool
switch dir {
case ascend:
for i := 0; i < len(n.items); i++ {
if start != nil && n.items[i].Less(start) {
continue
}
if len(n.children) > 0 {
if hit, ok = n.children[i].iterate(dir, start, stop, includeStart, hit, iter); !ok {
return hit, false
}
}
if !includeStart && !hit && start != nil && !start.Less(n.items[i]) {
hit = true
continue
}
hit = true
if stop != nil && !n.items[i].Less(stop) {
return hit, false
}
if !iter(n.items[i]) {
return hit, false
}
}
if len(n.children) > 0 {
if hit, ok = n.children[len(n.children)-1].iterate(dir, start, stop, includeStart, hit, iter); !ok {
return hit, false
}
}
case descend:
for i := len(n.items) - 1; i >= 0; i-- {
if start != nil && !n.items[i].Less(start) {
if !includeStart || hit || start.Less(n.items[i]) {
continue
}
}
if len(n.children) > 0 {
if hit, ok = n.children[i+1].iterate(dir, start, stop, includeStart, hit, iter); !ok {
return hit, false
}
}
if stop != nil && !stop.Less(n.items[i]) {
return hit, false // continue
}
hit = true
if !iter(n.items[i]) {
return hit, false
}
}
if len(n.children) > 0 {
if hit, ok = n.children[0].iterate(dir, start, stop, includeStart, hit, iter); !ok {
return hit, false
}
}
}
return hit, true
}
// Used for testing/debugging purposes.
func (n *node) print(w io.Writer, level int) {
fmt.Fprintf(w, "%sNODE:%v\n", strings.Repeat(" ", level), n.items)
for _, c := range n.children {
c.print(w, level+1)
}
}
// BTree is an implementation of a B-Tree.
//
// BTree stores Item instances in an ordered structure, allowing easy insertion,
// removal, and iteration.
//
// Write operations are not safe for concurrent mutation by multiple
// goroutines, but Read operations are.
type BTree struct {
degree int
length int
root *node
cow *copyOnWriteContext
}
// copyOnWriteContext pointers determine node ownership... a tree with a write
// context equivalent to a node's write context is allowed to modify that node.
// A tree whose write context does not match a node's is not allowed to modify
// it, and must create a new, writable copy (IE: it's a Clone).
//
// When doing any write operation, we maintain the invariant that the current
// node's context is equal to the context of the tree that requested the write.
// We do this by, before we descend into any node, creating a copy with the
// correct context if the contexts don't match.
//
// Since the node we're currently visiting on any write has the requesting
// tree's context, that node is modifiable in place. Children of that node may
// not share context, but before we descend into them, we'll make a mutable
// copy.
type copyOnWriteContext struct {
freelist *FreeList
}
// Clone clones the btree, lazily. Clone should not be called concurrently,
// but the original tree (t) and the new tree (t2) can be used concurrently
// once the Clone call completes.
//
// The internal tree structure of b is marked read-only and shared between t and
// t2. Writes to both t and t2 use copy-on-write logic, creating new nodes
// whenever one of b's original nodes would have been modified. Read operations
// should have no performance degredation. Write operations for both t and t2
// will initially experience minor slow-downs caused by additional allocs and
// copies due to the aforementioned copy-on-write logic, but should converge to
// the original performance characteristics of the original tree.
func (t *BTree) Clone() (t2 *BTree) {
// Create two entirely new copy-on-write contexts.
// This operation effectively creates three trees:
// the original, shared nodes (old b.cow)
// the new b.cow nodes
// the new out.cow nodes
cow1, cow2 := *t.cow, *t.cow
out := *t
t.cow = &cow1
out.cow = &cow2
return &out
}
// maxItems returns the max number of items to allow per node.
func (t *BTree) maxItems() int {
return t.degree*2 - 1
}
// minItems returns the min number of items to allow per node (ignored for the
// root node).
func (t *BTree) minItems() int {
return t.degree - 1
}
func (c *copyOnWriteContext) newNode() (n *node) {
n = c.freelist.newNode()
n.cow = c
return
}
func (c *copyOnWriteContext) freeNode(n *node) {
if n.cow == c {
// clear to allow GC
n.items.truncate(0)
n.children.truncate(0)
n.cow = nil
c.freelist.freeNode(n)
}
}
// ReplaceOrInsert adds the given item to the tree. If an item in the tree
// already equals the given one, it is removed from the tree and returned.
// Otherwise, nil is returned.
//
// nil cannot be added to the tree (will panic).
func (t *BTree) ReplaceOrInsert(item Item) Item {
if item == nil {
panic("nil item being added to BTree")
}
if t.root == nil {
t.root = t.cow.newNode()
t.root.items = append(t.root.items, item)
t.length++
return nil
} else {
t.root = t.root.mutableFor(t.cow)
if len(t.root.items) >= t.maxItems() {
item2, second := t.root.split(t.maxItems() / 2)
oldroot := t.root
t.root = t.cow.newNode()
t.root.items = append(t.root.items, item2)
t.root.children = append(t.root.children, oldroot, second)
}
}
out := t.root.insert(item, t.maxItems())
if out == nil {
t.length++
}
return out
}
// Delete removes an item equal to the passed in item from the tree, returning
// it. If no such item exists, returns nil.
func (t *BTree) Delete(item Item) Item {
return t.deleteItem(item, removeItem)
}
// DeleteMin removes the smallest item in the tree and returns it.
// If no such item exists, returns nil.
func (t *BTree) DeleteMin() Item {
return t.deleteItem(nil, removeMin)
}
// DeleteMax removes the largest item in the tree and returns it.
// If no such item exists, returns nil.
func (t *BTree) DeleteMax() Item {
return t.deleteItem(nil, removeMax)
}
func (t *BTree) deleteItem(item Item, typ toRemove) Item {
if t.root == nil || len(t.root.items) == 0 {
return nil
}
t.root = t.root.mutableFor(t.cow)
out := t.root.remove(item, t.minItems(), typ)
if len(t.root.items) == 0 && len(t.root.children) > 0 {
oldroot := t.root
t.root = t.root.children[0]
t.cow.freeNode(oldroot)
}
if out != nil {
t.length--
}
return out
}
// AscendRange calls the iterator for every value in the tree within the range
// [greaterOrEqual, lessThan), until iterator returns false.
func (t *BTree) AscendRange(greaterOrEqual, lessThan Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(ascend, greaterOrEqual, lessThan, true, false, iterator)
}
// AscendLessThan calls the iterator for every value in the tree within the range
// [first, pivot), until iterator returns false.
func (t *BTree) AscendLessThan(pivot Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(ascend, nil, pivot, false, false, iterator)
}
// AscendGreaterOrEqual calls the iterator for every value in the tree within
// the range [pivot, last], until iterator returns false.
func (t *BTree) AscendGreaterOrEqual(pivot Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(ascend, pivot, nil, true, false, iterator)
}
// Ascend calls the iterator for every value in the tree within the range
// [first, last], until iterator returns false.
func (t *BTree) Ascend(iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(ascend, nil, nil, false, false, iterator)
}
// DescendRange calls the iterator for every value in the tree within the range
// [lessOrEqual, greaterThan), until iterator returns false.
func (t *BTree) DescendRange(lessOrEqual, greaterThan Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(descend, lessOrEqual, greaterThan, true, false, iterator)
}
// DescendLessOrEqual calls the iterator for every value in the tree within the range
// [pivot, first], until iterator returns false.
func (t *BTree) DescendLessOrEqual(pivot Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(descend, pivot, nil, true, false, iterator)
}
// DescendGreaterThan calls the iterator for every value in the tree within
// the range [last, pivot), until iterator returns false.
func (t *BTree) DescendGreaterThan(pivot Item, iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(descend, nil, pivot, false, false, iterator)
}
// Descend calls the iterator for every value in the tree within the range
// [last, first], until iterator returns false.
func (t *BTree) Descend(iterator ItemIterator) {
if t.root == nil {
return
}
t.root.iterate(descend, nil, nil, false, false, iterator)
}
// Get looks for the key item in the tree, returning it. It returns nil if
// unable to find that item.
func (t *BTree) Get(key Item) Item {
if t.root == nil {
return nil
}
return t.root.get(key)
}
// Min returns the smallest item in the tree, or nil if the tree is empty.
func (t *BTree) Min() Item {
return min(t.root)
}
// Max returns the largest item in the tree, or nil if the tree is empty.
func (t *BTree) Max() Item {
return max(t.root)
}
// Has returns true if the given key is in the tree.
func (t *BTree) Has(key Item) bool {
return t.Get(key) != nil
}
// Len returns the number of items currently in the tree.
func (t *BTree) Len() int {
return t.length
}
// Int implements the Item interface for integers.
type Int int
// Less returns true if int(a) < int(b).
func (a Int) Less(b Item) bool {
return a < b.(Int)
}

View File

@ -0,0 +1,676 @@
package gbtree
import (
"flag"
"fmt"
"math/rand"
"reflect"
"sort"
"sync"
"testing"
"time"
)
func init() {
seed := time.Now().Unix()
fmt.Println(seed)
rand.Seed(seed)
}
// perm returns a random permutation of n Int items in the range [0, n).
func perm(n int) (out []Item) {
for _, v := range rand.Perm(n) {
out = append(out, Int(v))
}
return
}
// rang returns an ordered list of Int items in the range [0, n).
func rang(n int) (out []Item) {
for i := 0; i < n; i++ {
out = append(out, Int(i))
}
return
}
// all extracts all items from a tree in order as a slice.
func all(t *BTree) (out []Item) {
t.Ascend(func(a Item) bool {
out = append(out, a)
return true
})
return
}
// rangerev returns a reversed ordered list of Int items in the range [0, n).
func rangrev(n int) (out []Item) {
for i := n - 1; i >= 0; i-- {
out = append(out, Int(i))
}
return
}
// allrev extracts all items from a tree in reverse order as a slice.
func allrev(t *BTree) (out []Item) {
t.Descend(func(a Item) bool {
out = append(out, a)
return true
})
return
}
var btreeDegree = flag.Int("degree", 32, "B-Tree degree")
func TestBTree(t *testing.T) {
tr := New(*btreeDegree)
const treeSize = 10000
for i := 0; i < 10; i++ {
if min := tr.Min(); min != nil {
t.Fatalf("empty min, got %+v", min)
}
if max := tr.Max(); max != nil {
t.Fatalf("empty max, got %+v", max)
}
for _, item := range perm(treeSize) {
if x := tr.ReplaceOrInsert(item); x != nil {
t.Fatal("insert found item", item)
}
}
for _, item := range perm(treeSize) {
if x := tr.ReplaceOrInsert(item); x == nil {
t.Fatal("insert didn't find item", item)
}
}
if min, want := tr.Min(), Item(Int(0)); min != want {
t.Fatalf("min: want %+v, got %+v", want, min)
}
if max, want := tr.Max(), Item(Int(treeSize-1)); max != want {
t.Fatalf("max: want %+v, got %+v", want, max)
}
got := all(tr)
want := rang(treeSize)
if !reflect.DeepEqual(got, want) {
t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want)
}
gotrev := allrev(tr)
wantrev := rangrev(treeSize)
if !reflect.DeepEqual(gotrev, wantrev) {
t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want)
}
for _, item := range perm(treeSize) {
if x := tr.Delete(item); x == nil {
t.Fatalf("didn't find %v", item)
}
}
if got = all(tr); len(got) > 0 {
t.Fatalf("some left!: %v", got)
}
}
}
func ExampleBTree() {
tr := New(*btreeDegree)
for i := Int(0); i < 10; i++ {
tr.ReplaceOrInsert(i)
}
fmt.Println("len: ", tr.Len())
fmt.Println("get3: ", tr.Get(Int(3)))
fmt.Println("get100: ", tr.Get(Int(100)))
fmt.Println("del4: ", tr.Delete(Int(4)))
fmt.Println("del100: ", tr.Delete(Int(100)))
fmt.Println("replace5: ", tr.ReplaceOrInsert(Int(5)))
fmt.Println("replace100:", tr.ReplaceOrInsert(Int(100)))
fmt.Println("min: ", tr.Min())
fmt.Println("delmin: ", tr.DeleteMin())
fmt.Println("max: ", tr.Max())
fmt.Println("delmax: ", tr.DeleteMax())
fmt.Println("len: ", tr.Len())
// Output:
// len: 10
// get3: 3
// get100: <nil>
// del4: 4
// del100: <nil>
// replace5: 5
// replace100: <nil>
// min: 0
// delmin: 0
// max: 100
// delmax: 100
// len: 8
}
func TestDeleteMin(t *testing.T) {
tr := New(3)
for _, v := range perm(100) {
tr.ReplaceOrInsert(v)
}
var got []Item
for v := tr.DeleteMin(); v != nil; v = tr.DeleteMin() {
got = append(got, v)
}
if want := rang(100); !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestDeleteMax(t *testing.T) {
tr := New(3)
for _, v := range perm(100) {
tr.ReplaceOrInsert(v)
}
var got []Item
for v := tr.DeleteMax(); v != nil; v = tr.DeleteMax() {
got = append(got, v)
}
// Reverse our list.
for i := 0; i < len(got)/2; i++ {
got[i], got[len(got)-i-1] = got[len(got)-i-1], got[i]
}
if want := rang(100); !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestAscendRange(t *testing.T) {
tr := New(2)
for _, v := range perm(100) {
tr.ReplaceOrInsert(v)
}
var got []Item
tr.AscendRange(Int(40), Int(60), func(a Item) bool {
got = append(got, a)
return true
})
if want := rang(100)[40:60]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.AscendRange(Int(40), Int(60), func(a Item) bool {
if a.(Int) > 50 {
return false
}
got = append(got, a)
return true
})
if want := rang(100)[40:51]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestDescendRange(t *testing.T) {
tr := New(2)
for _, v := range perm(100) {
tr.ReplaceOrInsert(v)
}
var got []Item
tr.DescendRange(Int(60), Int(40), func(a Item) bool {
got = append(got, a)
return true
})
if want := rangrev(100)[39:59]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendrange:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.DescendRange(Int(60), Int(40), func(a Item) bool {
if a.(Int) < 50 {
return false
}
got = append(got, a)
return true
})
if want := rangrev(100)[39:50]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestAscendLessThan(t *testing.T) {
tr := New(*btreeDegree)
for _, v := range perm(100) {
tr.ReplaceOrInsert(v)
}
var got []Item
tr.AscendLessThan(Int(60), func(a Item) bool {
got = append(got, a)
return true
})
if want := rang(100)[:60]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.AscendLessThan(Int(60), func(a Item) bool {
if a.(Int) > 50 {
return false
}
got = append(got, a)
return true
})
if want := rang(100)[:51]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestDescendLessOrEqual(t *testing.T) {
tr := New(*btreeDegree)
for _, v := range perm(100) {
tr.ReplaceOrInsert(v)
}
var got []Item
tr.DescendLessOrEqual(Int(40), func(a Item) bool {
got = append(got, a)
return true
})
if want := rangrev(100)[59:]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendlessorequal:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.DescendLessOrEqual(Int(60), func(a Item) bool {
if a.(Int) < 50 {
return false
}
got = append(got, a)
return true
})
if want := rangrev(100)[39:50]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendlessorequal:\n got: %v\nwant: %v", got, want)
}
}
func TestAscendGreaterOrEqual(t *testing.T) {
tr := New(*btreeDegree)
for _, v := range perm(100) {
tr.ReplaceOrInsert(v)
}
var got []Item
tr.AscendGreaterOrEqual(Int(40), func(a Item) bool {
got = append(got, a)
return true
})
if want := rang(100)[40:]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.AscendGreaterOrEqual(Int(40), func(a Item) bool {
if a.(Int) > 50 {
return false
}
got = append(got, a)
return true
})
if want := rang(100)[40:51]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestDescendGreaterThan(t *testing.T) {
tr := New(*btreeDegree)
for _, v := range perm(100) {
tr.ReplaceOrInsert(v)
}
var got []Item
tr.DescendGreaterThan(Int(40), func(a Item) bool {
got = append(got, a)
return true
})
if want := rangrev(100)[:59]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendgreaterthan:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.DescendGreaterThan(Int(40), func(a Item) bool {
if a.(Int) < 50 {
return false
}
got = append(got, a)
return true
})
if want := rangrev(100)[:50]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendgreaterthan:\n got: %v\nwant: %v", got, want)
}
}
const benchmarkTreeSize = 10000
func BenchmarkInsert(b *testing.B) {
b.StopTimer()
insertP := perm(benchmarkTreeSize)
b.StartTimer()
i := 0
for i < b.N {
tr := New(*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
i++
if i >= b.N {
return
}
}
}
}
func BenchmarkDeleteInsert(b *testing.B) {
b.StopTimer()
insertP := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
tr.Delete(insertP[i%benchmarkTreeSize])
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
}
}
func BenchmarkDeleteInsertCloneOnce(b *testing.B) {
b.StopTimer()
insertP := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
}
tr = tr.Clone()
b.StartTimer()
for i := 0; i < b.N; i++ {
tr.Delete(insertP[i%benchmarkTreeSize])
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
}
}
func BenchmarkDeleteInsertCloneEachTime(b *testing.B) {
b.StopTimer()
insertP := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
tr = tr.Clone()
tr.Delete(insertP[i%benchmarkTreeSize])
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
}
}
func BenchmarkDelete(b *testing.B) {
b.StopTimer()
insertP := perm(benchmarkTreeSize)
removeP := perm(benchmarkTreeSize)
b.StartTimer()
i := 0
for i < b.N {
b.StopTimer()
tr := New(*btreeDegree)
for _, v := range insertP {
tr.ReplaceOrInsert(v)
}
b.StartTimer()
for _, item := range removeP {
tr.Delete(item)
i++
if i >= b.N {
return
}
}
if tr.Len() > 0 {
panic(tr.Len())
}
}
}
func BenchmarkGet(b *testing.B) {
b.StopTimer()
insertP := perm(benchmarkTreeSize)
removeP := perm(benchmarkTreeSize)
b.StartTimer()
i := 0
for i < b.N {
b.StopTimer()
tr := New(*btreeDegree)
for _, v := range insertP {
tr.ReplaceOrInsert(v)
}
b.StartTimer()
for _, item := range removeP {
tr.Get(item)
i++
if i >= b.N {
return
}
}
}
}
func BenchmarkGetCloneEachTime(b *testing.B) {
b.StopTimer()
insertP := perm(benchmarkTreeSize)
removeP := perm(benchmarkTreeSize)
b.StartTimer()
i := 0
for i < b.N {
b.StopTimer()
tr := New(*btreeDegree)
for _, v := range insertP {
tr.ReplaceOrInsert(v)
}
b.StartTimer()
for _, item := range removeP {
tr = tr.Clone()
tr.Get(item)
i++
if i >= b.N {
return
}
}
}
}
type byInts []Item
func (a byInts) Len() int {
return len(a)
}
func (a byInts) Less(i, j int) bool {
return a[i].(Int) < a[j].(Int)
}
func (a byInts) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
func BenchmarkAscend(b *testing.B) {
arr := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Sort(byInts(arr))
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := 0
tr.Ascend(func(item Item) bool {
if item.(Int) != arr[j].(Int) {
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
}
j++
return true
})
}
}
func BenchmarkDescend(b *testing.B) {
arr := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Sort(byInts(arr))
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := len(arr) - 1
tr.Descend(func(item Item) bool {
if item.(Int) != arr[j].(Int) {
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
}
j--
return true
})
}
}
func BenchmarkAscendRange(b *testing.B) {
arr := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Sort(byInts(arr))
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := 100
tr.AscendRange(Int(100), arr[len(arr)-100], func(item Item) bool {
if item.(Int) != arr[j].(Int) {
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
}
j++
return true
})
if j != len(arr)-100 {
b.Fatalf("expected: %v, got %v", len(arr)-100, j)
}
}
}
func BenchmarkDescendRange(b *testing.B) {
arr := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Sort(byInts(arr))
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := len(arr) - 100
tr.DescendRange(arr[len(arr)-100], Int(100), func(item Item) bool {
if item.(Int) != arr[j].(Int) {
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
}
j--
return true
})
if j != 100 {
b.Fatalf("expected: %v, got %v", len(arr)-100, j)
}
}
}
func BenchmarkAscendGreaterOrEqual(b *testing.B) {
arr := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Sort(byInts(arr))
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := 100
k := 0
tr.AscendGreaterOrEqual(Int(100), func(item Item) bool {
if item.(Int) != arr[j].(Int) {
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
}
j++
k++
return true
})
if j != len(arr) {
b.Fatalf("expected: %v, got %v", len(arr), j)
}
if k != len(arr)-100 {
b.Fatalf("expected: %v, got %v", len(arr)-100, k)
}
}
}
func BenchmarkDescendLessOrEqual(b *testing.B) {
arr := perm(benchmarkTreeSize)
tr := New(*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Sort(byInts(arr))
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := len(arr) - 100
k := len(arr)
tr.DescendLessOrEqual(arr[len(arr)-100], func(item Item) bool {
if item.(Int) != arr[j].(Int) {
b.Fatalf("mismatch: expected: %v, got %v", arr[j].(Int), item.(Int))
}
j--
k--
return true
})
if j != -1 {
b.Fatalf("expected: %v, got %v", -1, j)
}
if k != 99 {
b.Fatalf("expected: %v, got %v", 99, k)
}
}
}
const cloneTestSize = 10000
func cloneTest(t *testing.T, b *BTree, start int, p []Item, wg *sync.WaitGroup, trees *[]*BTree) {
t.Logf("Starting new clone at %v", start)
*trees = append(*trees, b)
for i := start; i < cloneTestSize; i++ {
b.ReplaceOrInsert(p[i])
if i%(cloneTestSize/5) == 0 {
wg.Add(1)
go cloneTest(t, b.Clone(), i+1, p, wg, trees)
}
}
wg.Done()
}
func TestCloneConcurrentOperations(t *testing.T) {
b := New(*btreeDegree)
trees := []*BTree{}
p := perm(cloneTestSize)
var wg sync.WaitGroup
wg.Add(1)
go cloneTest(t, b, 0, p, &wg, &trees)
wg.Wait()
want := rang(cloneTestSize)
t.Logf("Starting equality checks on %d trees", len(trees))
for i, tree := range trees {
if !reflect.DeepEqual(want, all(tree)) {
t.Errorf("tree %v mismatch", i)
}
}
t.Log("Removing half from first half")
toRemove := rang(cloneTestSize)[cloneTestSize/2:]
for i := 0; i < len(trees)/2; i++ {
tree := trees[i]
wg.Add(1)
go func() {
for _, item := range toRemove {
tree.Delete(item)
}
wg.Done()
}()
}
wg.Wait()
t.Log("Checking all values again")
for i, tree := range trees {
var wantpart []Item
if i < len(trees)/2 {
wantpart = want[:cloneTestSize/2]
} else {
wantpart = want
}
if got := all(tree); !reflect.DeepEqual(wantpart, got) {
t.Errorf("tree %v mismatch, want %v got %v", i, len(want), len(got))
}
}
}

View File

@ -0,0 +1,209 @@
package glist
import (
"container/list"
"sync"
)
// 变长双向链表
type SafeList struct {
sync.RWMutex
L *list.List
}
// 获得一个变长链表指针
func NewSafeList() *SafeList {
return &SafeList{L: list.New()}
}
// 往链表头入栈数据项
func (this *SafeList) PushFront(v interface{}) *list.Element {
this.Lock()
e := this.L.PushFront(v)
this.Unlock()
return e
}
// 往链表尾入栈数据项
func (this *SafeList) PushBack(v interface{}) *list.Element {
this.Lock()
r := this.L.PushBack(v)
this.Unlock()
return r
}
// 在list 中元素mark之后插入一个值为v的元素并返回该元素如果mark不是list中元素则list不改变。
func (this *SafeList) InsertAfter(v interface{}, mark *list.Element) *list.Element {
this.Lock()
r := this.L.InsertAfter(v, mark)
this.Unlock()
return r
}
// 在list 中元素mark之前插入一个值为v的元素并返回该元素如果mark不是list中元素则list不改变。
func (this *SafeList) InsertBefore(v interface{}, mark *list.Element) *list.Element {
this.Lock()
r := this.L.InsertBefore(v, mark)
this.Unlock()
return r
}
// 批量往链表头入栈数据项
func (this *SafeList) BatchPushFront(vs []interface{}) {
this.Lock()
for _, item := range vs {
this.L.PushFront(item)
}
this.Unlock()
}
// 从链表尾端出栈数据项(删除)
func (this *SafeList) PopBack() interface{} {
this.Lock()
if elem := this.L.Back(); elem != nil {
item := this.L.Remove(elem)
this.Unlock()
return item
}
this.Unlock()
return nil
}
// 批量从链表尾端出栈数据项(删除)
func (this *SafeList) BatchPopBack(max int) []interface{} {
this.Lock()
count := this.L.Len()
if count == 0 {
this.Unlock()
return []interface{}{}
}
if count > max {
count = max
}
items := make([]interface{}, 0, count)
for i := 0; i < count; i++ {
item := this.L.Remove(this.L.Back())
items = append(items, item)
}
this.Unlock()
return items
}
// 批量从链表尾端依次获取所有数据
func (this *SafeList) PopBackAll() []interface{} {
this.Lock()
count := this.L.Len()
if count == 0 {
this.Unlock()
return []interface{}{}
}
items := make([]interface{}, 0, count)
for i := 0; i < count; i++ {
item := this.L.Remove(this.L.Back())
items = append(items, item)
}
this.Unlock()
return items
}
// 删除数据项
func (this *SafeList) Remove(e *list.Element) interface{} {
this.Lock()
r := this.L.Remove(e)
this.Unlock()
return r
}
// 删除所有数据项
func (this *SafeList) RemoveAll() {
this.Lock()
this.L = list.New()
this.Unlock()
}
// 从链表头获取所有数据(不删除)
func (this *SafeList) FrontAll() []interface{} {
this.RLock()
count := this.L.Len()
if count == 0 {
this.RUnlock()
return []interface{}{}
}
items := make([]interface{}, 0, count)
for e := this.L.Front(); e != nil; e = e.Next() {
items = append(items, e.Value)
}
this.RUnlock()
return items
}
// 从链表尾获取所有数据(不删除)
func (this *SafeList) BackAll() []interface{} {
this.RLock()
count := this.L.Len()
if count == 0 {
this.RUnlock()
return []interface{}{}
}
items := make([]interface{}, 0, count)
for e := this.L.Back(); e != nil; e = e.Prev() {
items = append(items, e.Value)
}
this.RUnlock()
return items
}
// 获取链表头值(不删除)
func (this *SafeList) FrontItem() interface{} {
this.RLock()
if f := this.L.Front(); f != nil {
this.RUnlock()
return f.Value
}
this.RUnlock()
return nil
}
// 获取链表尾值(不删除)
func (this *SafeList) BackItem() interface{} {
this.RLock()
if f := this.L.Back(); f != nil {
this.RUnlock()
return f.Value
}
this.RUnlock()
return nil
}
// 获取表头指针
func (this *SafeList) Front() *list.Element {
this.RLock()
r := this.L.Front()
this.RUnlock()
return r
}
// 获取表位指针
func (this *SafeList) Back() *list.Element {
this.RLock()
r := this.L.Back()
this.RUnlock()
return r
}
// 获取链表长度
func (this *SafeList) Len() int {
this.RLock()
length := this.L.Len()
this.RUnlock()
return length
}

View File

@ -0,0 +1,132 @@
package gmap
import (
"sync"
)
type IntBoolMap struct {
sync.RWMutex
m map[int]bool
}
func NewIntBoolMap() *IntBoolMap {
return &IntBoolMap{
m: make(map[int]bool),
}
}
// 哈希表克隆
func (this *IntBoolMap) Clone() *map[int]bool {
m := make(map[int]bool)
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *IntBoolMap) Set(key int, val bool) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *IntBoolMap) BatchSet(m map[int]bool) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *IntBoolMap) Get(key int) (bool) {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *IntBoolMap) Remove(key int) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *IntBoolMap) BatchRemove(keys []int) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *IntBoolMap) GetAndRemove(key int) (bool) {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *IntBoolMap) Keys() []int {
this.RLock()
keys := make([]int, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
//func (this *IntBoolMap) Values() []bool {
// this.RLock()
// vals := make([]bool, 0)
// for _, val := range this.m {
// vals = append(vals, val)
// }
// this.RUnlock()
// return vals
//}
// 是否存在某个键
func (this *IntBoolMap) Contains(key int) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *IntBoolMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *IntBoolMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *IntBoolMap) Clear() {
this.Lock()
this.m = make(map[int]bool)
this.Unlock()
}

View File

@ -0,0 +1,132 @@
package gmap
import (
"sync"
)
type IntIntMap struct {
sync.RWMutex
m map[int]int
}
func NewIntIntMap() *IntIntMap {
return &IntIntMap{
m: make(map[int]int),
}
}
// 哈希表克隆
func (this *IntIntMap) Clone() *map[int]int {
m := make(map[int]int)
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *IntIntMap) Set(key int, val int) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *IntIntMap) BatchSet(m map[int]int) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *IntIntMap) Get(key int) (int) {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *IntIntMap) Remove(key int) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *IntIntMap) BatchRemove(keys []int) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *IntIntMap) GetAndRemove(key int) (int) {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *IntIntMap) Keys() []int {
this.RLock()
keys := make([]int, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
func (this *IntIntMap) Values() []int {
this.RLock()
vals := make([]int, 0)
for _, val := range this.m {
vals = append(vals, val)
}
this.RUnlock()
return vals
}
// 是否存在某个键
func (this *IntIntMap) Contains(key int) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *IntIntMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *IntIntMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *IntIntMap) Clear() {
this.Lock()
this.m = make(map[int]int)
this.Unlock()
}

View File

@ -0,0 +1,132 @@
package gmap
import (
"sync"
)
type IntInterfaceMap struct {
sync.RWMutex
m map[int]interface{}
}
func NewIntInterfaceMap() *IntInterfaceMap {
return &IntInterfaceMap{
m: make(map[int]interface{}),
}
}
// 哈希表克隆
func (this *IntInterfaceMap) Clone() *map[int]interface{} {
m := make(map[int]interface{})
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *IntInterfaceMap) Set(key int, val interface{}) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *IntInterfaceMap) BatchSet(m map[int]interface{}) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *IntInterfaceMap) Get(key int) (interface{}) {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *IntInterfaceMap) Remove(key int) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *IntInterfaceMap) BatchRemove(keys []int) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *IntInterfaceMap) GetAndRemove(key int) (interface{}) {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *IntInterfaceMap) Keys() []int {
this.RLock()
keys := make([]int, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
func (this *IntInterfaceMap) Values() []interface{} {
this.RLock()
vals := make([]interface{}, 0)
for _, val := range this.m {
vals = append(vals, val)
}
this.RUnlock()
return vals
}
// 是否存在某个键
func (this *IntInterfaceMap) Contains(key int) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *IntInterfaceMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *IntInterfaceMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *IntInterfaceMap) Clear() {
this.Lock()
this.m = make(map[int]interface{})
this.Unlock()
}

View File

@ -0,0 +1,132 @@
package gmap
import (
"sync"
)
type IntStringMap struct {
sync.RWMutex
m map[int]string
}
func NewIntStringMap() *IntStringMap {
return &IntStringMap{
m: make(map[int]string),
}
}
// 哈希表克隆
func (this *IntStringMap) Clone() *map[int]string {
m := make(map[int]string)
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *IntStringMap) Set(key int, val string) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *IntStringMap) BatchSet(m map[int]string) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *IntStringMap) Get(key int) (string) {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *IntStringMap) Remove(key int) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *IntStringMap) BatchRemove(keys []int) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *IntStringMap) GetAndRemove(key int) (string) {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *IntStringMap) Keys() []int {
this.RLock()
keys := make([]int, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
func (this *IntStringMap) Values() []string {
this.RLock()
vals := make([]string, 0)
for _, val := range this.m {
vals = append(vals, val)
}
this.RUnlock()
return vals
}
// 是否存在某个键
func (this *IntStringMap) Contains(key int) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *IntStringMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *IntStringMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *IntStringMap) Clear() {
this.Lock()
this.m = make(map[int]string)
this.Unlock()
}

View File

@ -0,0 +1,131 @@
package gmap
import (
"sync"
)
type InterfaceInterfaceMap struct {
sync.RWMutex
m map[interface{}]interface{}
}
func NewInterfaceInterfaceMap() *InterfaceInterfaceMap {
return &InterfaceInterfaceMap{
m: make(map[interface{}]interface{}),
}
}
// 哈希表克隆
func (this *InterfaceInterfaceMap) Clone() *map[interface{}]interface{} {
m := make(map[interface{}]interface{})
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *InterfaceInterfaceMap) Set(key interface{}, val interface{}) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *InterfaceInterfaceMap) BatchSet(m map[interface{}]interface{}) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *InterfaceInterfaceMap) Get(key interface{}) (interface{}) {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *InterfaceInterfaceMap) Remove(key interface{}) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *InterfaceInterfaceMap) BatchRemove(keys []interface{}) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *InterfaceInterfaceMap) GetAndRemove(key interface{}) (interface{}) {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *InterfaceInterfaceMap) Keys() []interface{} {
this.RLock()
keys := make([]interface{}, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
func (this *InterfaceInterfaceMap) Values() []interface{} {
this.RLock()
vals := make([]interface{}, 0)
for _, val := range this.m {
vals = append(vals, val)
}
this.RUnlock()
return vals
}
// 是否存在某个键
func (this *InterfaceInterfaceMap) Contains(key interface{}) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *InterfaceInterfaceMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *InterfaceInterfaceMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *InterfaceInterfaceMap) Clear() {
this.Lock()
this.m = make(map[interface{}]interface{})
this.Unlock()
}

View File

@ -0,0 +1,131 @@
package gmap
import (
"sync"
)
type StringBoolMap struct {
sync.RWMutex
m map[string]bool
}
func NewStringBoolMap() *StringBoolMap {
return &StringBoolMap{
m: make(map[string]bool),
}
}
// 哈希表克隆
func (this *StringBoolMap) Clone() *map[string]bool {
m := make(map[string]bool)
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *StringBoolMap) Set(key string, val bool) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *StringBoolMap) BatchSet(m map[string]bool) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *StringBoolMap) Get(key string) (bool) {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *StringBoolMap) Remove(key string) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *StringBoolMap) BatchRemove(keys []string) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *StringBoolMap) GetAndRemove(key string) (bool) {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *StringBoolMap) Keys() []string {
this.RLock()
keys := make([]string, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
//func (this *StringBoolMap) Values() []bool {
// this.RLock()
// vals := make([]bool, 0)
// for _, val := range this.m {
// vals = append(vals, val)
// }
// this.RUnlock()
// return vals
//}
// 是否存在某个键
func (this *StringBoolMap) Contains(key string) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *StringBoolMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *StringBoolMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *StringBoolMap) Clear() {
this.Lock()
this.m = make(map[string]bool)
this.Unlock()
}

View File

@ -0,0 +1,132 @@
package gmap
import (
"sync"
)
type StringIntMap struct {
sync.RWMutex
m map[string]int
}
func NewStringIntMap() *StringIntMap {
return &StringIntMap{
m: make(map[string]int),
}
}
// 哈希表克隆
func (this *StringIntMap) Clone() *map[string]int {
m := make(map[string]int)
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *StringIntMap) Set(key string, val int) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *StringIntMap) BatchSet(m map[string]int) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *StringIntMap) Get(key string) (int) {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *StringIntMap) Remove(key string) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *StringIntMap) BatchRemove(keys []string) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *StringIntMap) GetAndRemove(key string) (int) {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *StringIntMap) Keys() []string {
this.RLock()
keys := make([]string, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
func (this *StringIntMap) Values() []int {
this.RLock()
vals := make([]int, 0)
for _, val := range this.m {
vals = append(vals, val)
}
this.RUnlock()
return vals
}
// 是否存在某个键
func (this *StringIntMap) Contains(key string) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *StringIntMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *StringIntMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *StringIntMap) Clear() {
this.Lock()
this.m = make(map[string]int)
this.Unlock()
}

View File

@ -0,0 +1,131 @@
package gmap
import (
"sync"
)
type StringInterfaceMap struct {
sync.RWMutex
m map[string]interface{}
}
func NewStringInterfaceMap() *StringInterfaceMap {
return &StringInterfaceMap{
m: make(map[string]interface{}),
}
}
// 哈希表克隆
func (this *StringInterfaceMap) Clone() *map[string]interface{} {
m := make(map[string]interface{})
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *StringInterfaceMap) Set(key string, val interface{}) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *StringInterfaceMap) BatchSet(m map[string]interface{}) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *StringInterfaceMap) Get(key string) interface{} {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *StringInterfaceMap) Remove(key string) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *StringInterfaceMap) BatchRemove(keys []string) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *StringInterfaceMap) GetAndRemove(key string) interface{} {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *StringInterfaceMap) Keys() []string {
this.RLock()
keys := make([]string, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
func (this *StringInterfaceMap) Values() []interface{} {
this.RLock()
vals := make([]interface{}, 0)
for _, val := range this.m {
vals = append(vals, val)
}
this.RUnlock()
return vals
}
// 是否存在某个键
func (this *StringInterfaceMap) Contains(key string) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *StringInterfaceMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *StringInterfaceMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *StringInterfaceMap) Clear() {
this.Lock()
this.m = make(map[string]interface{})
this.Unlock()
}

View File

@ -0,0 +1,131 @@
package gmap
import (
"sync"
)
type StringStringMap struct {
sync.RWMutex
m map[string]string
}
func NewStringStringMap() *StringStringMap {
return &StringStringMap{
m: make(map[string]string),
}
}
// 哈希表克隆
func (this *StringStringMap) Clone() *map[string]string {
m := make(map[string]string)
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *StringStringMap) Set(key string, val string) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *StringStringMap) BatchSet(m map[string]string) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *StringStringMap) Get(key string) string {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *StringStringMap) Remove(key string) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *StringStringMap) BatchRemove(keys []string) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *StringStringMap) GetAndRemove(key string) string {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *StringStringMap) Keys() []string {
this.RLock()
keys := make([]string, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
func (this *StringStringMap) Values() []string {
this.RLock()
vals := make([]string, 0)
for _, val := range this.m {
vals = append(vals, val)
}
this.RUnlock()
return vals
}
// 是否存在某个键
func (this *StringStringMap) Contains(key string) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *StringStringMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *StringStringMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *StringStringMap) Clear() {
this.Lock()
this.m = make(map[string]string)
this.Unlock()
}

View File

@ -0,0 +1,132 @@
package gmap
import (
"sync"
)
type UintInterfaceMap struct {
sync.RWMutex
m map[uint]interface{}
}
func NewUintInterfaceMap() *UintInterfaceMap {
return &UintInterfaceMap{
m: make(map[uint]interface{}),
}
}
// 哈希表克隆
func (this *UintInterfaceMap) Clone() *map[uint]interface{} {
m := make(map[uint]interface{})
this.RLock()
for k, v := range this.m {
m[k] = v
}
this.RUnlock()
return &m
}
// 设置键值对
func (this *UintInterfaceMap) Set(key uint, val interface{}) {
this.Lock()
this.m[key] = val
this.Unlock()
}
// 批量设置键值对
func (this *UintInterfaceMap) BatchSet(m map[uint]interface{}) {
this.Lock()
for k, v := range m {
this.m[k] = v
}
this.Unlock()
}
// 获取键值
func (this *UintInterfaceMap) Get(key uint) (interface{}) {
this.RLock()
val, _ := this.m[key]
this.RUnlock()
return val
}
// 删除键值对
func (this *UintInterfaceMap) Remove(key uint) {
this.Lock()
delete(this.m, key)
this.Unlock()
}
// 批量删除键值对
func (this *UintInterfaceMap) BatchRemove(keys []uint) {
this.Lock()
for _, key := range keys {
delete(this.m, key)
}
this.Unlock()
}
// 返回对应的键值,并删除该键值
func (this *UintInterfaceMap) GetAndRemove(key uint) (interface{}) {
this.Lock()
val, exists := this.m[key]
if exists {
delete(this.m, key)
}
this.Unlock()
return val
}
// 返回键列表
func (this *UintInterfaceMap) Keys() []uint {
this.RLock()
keys := make([]uint, 0)
for key, _ := range this.m {
keys = append(keys, key)
}
this.RUnlock()
return keys
}
// 返回值列表(注意是随机排序)
func (this *UintInterfaceMap) Values() []interface{} {
this.RLock()
vals := make([]interface{}, 0)
for _, val := range this.m {
vals = append(vals, val)
}
this.RUnlock()
return vals
}
// 是否存在某个键
func (this *UintInterfaceMap) Contains(key uint) bool {
this.RLock()
_, exists := this.m[key]
this.RUnlock()
return exists
}
// 哈希表大小
func (this *UintInterfaceMap) Size() int {
this.RLock()
len := len(this.m)
this.RUnlock()
return len
}
// 哈希表是否为空
func (this *UintInterfaceMap) IsEmpty() bool {
this.RLock()
empty := (len(this.m) == 0)
this.RUnlock()
return empty
}
// 清空哈希表
func (this *UintInterfaceMap) Clear() {
this.Lock()
this.m = make(map[uint]interface{})
this.Unlock()
}

View File

@ -0,0 +1,108 @@
package gset
import (
"fmt"
"sync"
)
type Int64Set struct {
sync.RWMutex
M map[int64]struct{}
}
func NewInt64Set() *Int64Set {
return &Int64Set{M: make(map[int64]struct{})}
}
// 设置键
func (this *Int64Set) Add(item int64) *Int64Set {
if this.Contains(item) {
return this
}
this.Lock()
this.M[item] = struct{}{}
this.Unlock()
return this
}
// 批量添加设置键
func (this *Int64Set) BatchAdd(items []int64) *Int64Set {
count := len(items)
if count == 0 {
return this
}
todo := make([]int64, 0, count)
this.RLock()
for i := 0; i < count; i++ {
_, exists := this.M[items[i]]
if exists {
continue
}
todo = append(todo, items[i])
}
this.RUnlock()
count = len(todo)
if count == 0 {
return this
}
this.Lock()
for i := 0; i < count; i++ {
this.M[todo[i]] = struct{}{}
}
this.Unlock()
return this
}
// 键是否存在
func (this *Int64Set) Contains(item int64) bool {
this.RLock()
_, exists := this.M[item]
this.RUnlock()
return exists
}
// 删除键值对
func (this *Int64Set) Remove(key int64) {
this.Lock()
delete(this.M, key)
this.Unlock()
}
// 大小
func (this *Int64Set) Size() int {
this.RLock()
l := len(this.M)
this.RUnlock()
return l
}
// 清空set
func (this *Int64Set) Clear() {
this.Lock()
this.M = make(map[int64]struct{})
this.Unlock()
}
// 转换为数组
func (this *Int64Set) Slice() []int64 {
this.RLock()
ret := make([]int64, len(this.M))
i := 0
for item := range this.M {
ret[i] = item
i++
}
this.RUnlock()
return ret
}
// 转换为字符串
func (this *Int64Set) String() string {
s := this.Slice()
return fmt.Sprint(s)
}

View File

@ -0,0 +1,108 @@
package gset
import (
"fmt"
"sync"
)
type IntSet struct {
sync.RWMutex
M map[int]struct{}
}
func NewIntSet() *IntSet {
return &IntSet{M: make(map[int]struct{})}
}
// 设置键
func (this *IntSet) Add(item int) *IntSet {
if this.Contains(item) {
return this
}
this.Lock()
this.M[item] = struct{}{}
this.Unlock()
return this
}
// 批量添加设置键
func (this *IntSet) BatchAdd(items []int) *IntSet {
count := len(items)
if count == 0 {
return this
}
todo := make([]int, 0, count)
this.RLock()
for i := 0; i < count; i++ {
_, exists := this.M[items[i]]
if exists {
continue
}
todo = append(todo, items[i])
}
this.RUnlock()
count = len(todo)
if count == 0 {
return this
}
this.Lock()
for i := 0; i < count; i++ {
this.M[todo[i]] = struct{}{}
}
this.Unlock()
return this
}
// 键是否存在
func (this *IntSet) Contains(item int) bool {
this.RLock()
_, exists := this.M[item]
this.RUnlock()
return exists
}
// 删除键值对
func (this *IntSet) Remove(key int) {
this.Lock()
delete(this.M, key)
this.Unlock()
}
// 大小
func (this *IntSet) Size() int {
this.RLock()
l := len(this.M)
this.RUnlock()
return l
}
// 清空set
func (this *IntSet) Clear() {
this.Lock()
this.M = make(map[int]struct{})
this.Unlock()
}
// 转换为数组
func (this *IntSet) Slice() []int {
this.RLock()
ret := make([]int, len(this.M))
i := 0
for item := range this.M {
ret[i] = item
i++
}
this.RUnlock()
return ret
}
// 转换为字符串
func (this *IntSet) String() string {
s := this.Slice()
return fmt.Sprint(s)
}

View File

@ -0,0 +1,108 @@
package gset
import (
"fmt"
"sync"
)
type InterfaceSet struct {
sync.RWMutex
M map[interface{}]struct{}
}
func NewInterfaceSet() *InterfaceSet {
return &InterfaceSet{M: make(map[interface{}]struct{})}
}
// 设置键
func (this *InterfaceSet) Add(item interface{}) *InterfaceSet {
if this.Contains(item) {
return this
}
this.Lock()
this.M[item] = struct{}{}
this.Unlock()
return this
}
// 批量添加设置键
func (this *InterfaceSet) BatchAdd(items []interface{}) *InterfaceSet {
count := len(items)
if count == 0 {
return this
}
todo := make([]interface{}, 0, count)
this.RLock()
for i := 0; i < count; i++ {
_, exists := this.M[items[i]]
if exists {
continue
}
todo = append(todo, items[i])
}
this.RUnlock()
count = len(todo)
if count == 0 {
return this
}
this.Lock()
for i := 0; i < count; i++ {
this.M[todo[i]] = struct{}{}
}
this.Unlock()
return this
}
// 键是否存在
func (this *InterfaceSet) Contains(item interface{}) bool {
this.RLock()
_, exists := this.M[item]
this.RUnlock()
return exists
}
// 删除键值对
func (this *InterfaceSet) Remove(key interface{}) {
this.Lock()
delete(this.M, key)
this.Unlock()
}
// 大小
func (this *InterfaceSet) Size() int {
this.RLock()
l := len(this.M)
this.RUnlock()
return l
}
// 清空set
func (this *InterfaceSet) Clear() {
this.Lock()
this.M = make(map[interface{}]struct{})
this.Unlock()
}
// 转换为数组
func (this *InterfaceSet) Slice() []interface{} {
this.RLock()
ret := make([]interface{}, len(this.M))
i := 0
for item := range this.M {
ret[i] = item
i++
}
this.RUnlock()
return ret
}
// 转换为字符串
func (this *InterfaceSet) String() string {
s := this.Slice()
return fmt.Sprint(s)
}

View File

@ -0,0 +1,108 @@
package gset
import (
"fmt"
"sync"
)
type StringSet struct {
sync.RWMutex
M map[string]struct{}
}
func NewStringSet() *StringSet {
return &StringSet{M: make(map[string]struct{})}
}
// 设置键
func (this *StringSet) Add(item string) *StringSet {
if this.Contains(item) {
return this
}
this.Lock()
this.M[item] = struct{}{}
this.Unlock()
return this
}
// 批量添加设置键
func (this *StringSet) BatchAdd(items []string) *StringSet {
count := len(items)
if count == 0 {
return this
}
todo := make([]string, 0, count)
this.RLock()
for i := 0; i < count; i++ {
_, exists := this.M[items[i]]
if exists {
continue
}
todo = append(todo, items[i])
}
this.RUnlock()
count = len(todo)
if count == 0 {
return this
}
this.Lock()
for i := 0; i < count; i++ {
this.M[todo[i]] = struct{}{}
}
this.Unlock()
return this
}
// 键是否存在
func (this *StringSet) Contains(item string) bool {
this.RLock()
_, exists := this.M[item]
this.RUnlock()
return exists
}
// 删除键值对
func (this *StringSet) Remove(key string) {
this.Lock()
delete(this.M, key)
this.Unlock()
}
// 大小
func (this *StringSet) Size() int {
this.RLock()
l := len(this.M)
this.RUnlock()
return l
}
// 清空set
func (this *StringSet) Clear() {
this.Lock()
this.M = make(map[string]struct{})
this.Unlock()
}
// 转换为数组
func (this *StringSet) Slice() []string {
this.RLock()
ret := make([]string, len(this.M))
i := 0
for item := range this.M {
ret[i] = item
i++
}
this.RUnlock()
return ret
}
// 转换为字符串
func (this *StringSet) String() string {
s := this.Slice()
return fmt.Sprint(s)
}

348
g/database/gdb/db.go Normal file
View File

@ -0,0 +1,348 @@
// 对常用关系数据库的封装管理包
package gdb
import (
"database/sql"
"errors"
"fmt"
"g/util/grand"
"sync"
"g/os/glog"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
"g/os/gcache"
)
const (
OPTION_INSERT = 0
OPTION_REPLACE = 1
OPTION_SAVE = 2
OPTION_IGNORE = 3
)
// 数据库配置包内对象
var config struct {
sync.RWMutex
c Config // 数据库配置
d string // 默认数据库分组名称
}
// 数据库操作接口
type Link interface {
Open (c *ConfigNode) (*sql.DB, error)
Close() error
Query(q string, args ...interface{}) (*sql.Rows, error)
Exec(q string, args ...interface{}) (sql.Result, error)
Prepare(q string) (*sql.Stmt, error)
GetAll(q string, args ...interface{}) (*List, error)
GetOne(q string, args ...interface{}) (*Map, error)
GetValue(q string, args ...interface{}) (interface{}, error)
PingMaster() error
PingSlave() error
SetMaxIdleConns(n int)
SetMaxOpenConns(n int)
setMaster(master *sql.DB)
setSlave(slave *sql.DB)
setQuoteChar(left string, right string)
setLink(link Link)
getQuoteCharLeft () string
getQuoteCharRight () string
handleSqlBeforeExec(q *string) *string
Begin() (*sql.Tx, error)
Commit() error
Rollback() error
insert(table string, data *Map, option uint8) (sql.Result, error)
Insert(table string, data *Map) (sql.Result, error)
Replace(table string, data *Map) (sql.Result, error)
Save(table string, data *Map) (sql.Result, error)
batchInsert(table string, list *List, batch int, option uint8) error
BatchInsert(table string, list *List, batch int) error
BatchReplace(table string, list *List, batch int) error
BatchSave(table string, list *List, batch int) error
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
Table(tables string) (*gLinkOp)
}
// 数据库链接对象
type dbLink struct {
link Link
transaction *sql.Tx
master *sql.DB
slave *sql.DB
charl string
charr string
}
// 数据库配置
type Config map[string]ConfigGroup
// 数据库集群配置
type ConfigGroup []ConfigNode
// 数据库单项配置
type ConfigNode struct {
Host string // 地址
Port string // 端口
User string // 账号
Pass string // 密码
Name string // 数据库名称
Type string // 数据库类型mysql, sqlite, mssql, pgsql, oracle(目前仅支持mysql)
Role string // (可选默认为master)数据库的角色用于主从操作分离至少需要有一个master参数值master, slave
Charset string // (可选,默认为 utf-8)编码,默认为 utf-8
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
Linkinfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
}
// 关联数组,绑定一条数据表记录
type Map map[string]interface{}
// 关联数组列表(索引从0开始的数组),绑定多条记录
type List []Map
// 数据库集群配置示例,支持主从处理,多数据库集群支持
/*
var DatabaseConfiguration = Config {
// 数据库集群配置名称
"default" : ConfigGroup {
{
Host : "192.168.1.100",
Port : "3306",
User : "root",
Pass : "123456",
Name : "test",
Type : "mysql",
Role : "master",
Charset : "utf-8",
Priority : 100,
},
{
Host : "192.168.1.101",
Port : "3306",
User : "root",
Pass : "123456",
Name : "test",
Type : "mysql",
Role : "slave",
Charset : "utf-8",
Priority : 100,
},
},
}
*/
// 包初始化
func init() {
config.c = make(Config)
config.d = "default"
}
// 设置当前应用的数据库配置信息,进行全局数据库配置覆盖操作
// 支持三种数据类型的输入参数Config, ConfigGroup, ConfigNode
func SetConfig (c interface{}) error {
config.Lock()
defer config.Unlock()
switch c.(type) {
case Config:
config.c = c.(Config)
case ConfigGroup:
config.c = Config {"default" : c.(ConfigGroup)}
case ConfigNode:
config.c = Config {"default" : ConfigGroup { c.(ConfigNode) }}
default:
return errors.New("invalid config type, types should be in: Config, ConfigGroup, ConfigNode")
}
return nil
}
// 添加一台数据库服务器配置
func AddConfigNode (group string, node ConfigNode) {
config.Lock()
config.c[group] = append(config.c[group], node)
config.Unlock()
}
// 添加数据库服务器集群配置
func AddConfigGroup (group string, nodes ConfigGroup) {
config.Lock()
config.c[group] = nodes
config.Unlock()
}
// 添加默认链接的一台数据库服务器配置
func AddDefaultConfigNode (node ConfigNode) {
AddConfigNode("default", node)
}
// 添加默认链接的数据库服务器集群配置
func AddDefaultConfigGroup (nodes ConfigGroup) {
AddConfigGroup("default", nodes)
}
// 设置默认链接的数据库链接配置项(默认是 default)
func SetDefaultGroup (groupName string) {
config.Lock()
config.d = groupName
config.Unlock()
}
// 根据配置项获取一个数据库操作对象单例
func instance (groupName string) (Link, error) {
instanceName := "gdb_instance_" + groupName
result := gcache.Get(instanceName)
if result == nil {
link, err := NewByGroup(groupName)
if err == nil {
gcache.Set(instanceName, link, 0)
return link, nil
} else {
return nil, err
}
} else {
return result.(Link), nil
}
}
// 获得默认的数据库操作对象单例
func Instance () (Link, error) {
return instance(config.d)
}
// 获得指定配置项的数据库草最对象单例
func InstanceByGroup(groupName string) (Link, error) {
return instance(groupName)
}
// 使用默认选项进行连接数据库集群配置项default
func New() (Link, error) {
return NewByGroup(config.d)
}
// 根据数据库配置项创建一个数据库操作对象
func NewByGroup(groupName string) (Link, error) {
config.RLock()
defer config.RUnlock()
if len(config.c) < 1 {
return nil, errors.New("empty database configuration")
}
if list, ok := config.c[groupName]; ok {
// 将master, slave集群列表拆分出来
masterList := make(ConfigGroup, 0)
slaveList := make(ConfigGroup, 0)
for i := 0; i < len(list); i++ {
if list[i].Role == "slave" {
slaveList = append(slaveList, list[i])
} else {
// 默认配置项的角色为master
masterList = append(masterList, list[i])
}
}
if len(masterList) < 1 {
return nil, errors.New("at least one master node configuration's need to make sense")
}
masterNode := getConfigNodeByPriority(&masterList)
var slaveNode *ConfigNode
if len(slaveList) > 0 {
slaveNode = getConfigNodeByPriority(&slaveList)
}
return newLink(masterNode, slaveNode)
} else {
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", groupName))
}
}
// 根据单点数据库配置获得一个数据库草最对象
func NewByConfigNode(node ConfigNode) (Link, error) {
return newLink (&node, nil)
}
// 按照负载均衡算法(优先级配置)从数据库集群中选择一个配置节点出来使用
func getConfigNodeByPriority (cg *ConfigGroup) *ConfigNode {
if len(*cg) < 2 {
return &(*cg)[0]
}
var total int
for i := 0; i < len(*cg); i++ {
total += (*cg)[i].Priority * 100
}
r := grand.Rand(0, total)
min := 0
max := 0
for i := 0; i < len(*cg); i++ {
max = min + (*cg)[i].Priority * 100
//fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max)
if r >= min && r < max {
return &(*cg)[i]
} else {
min = max
}
}
return nil
}
// 创建数据库链接对象
func newLink (masterNode *ConfigNode, slaveNode *ConfigNode) (Link, error) {
var link Link
switch masterNode.Type {
case "mysql":
link = Link(&mysqlLink{})
case "pgsql":
link = Link(&pgsqlLink{})
default:
return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", masterNode.Type))
}
master, err := link.Open(masterNode)
if err != nil {
glog.Fatal(err)
}
slave := master
if slaveNode != nil {
slave, err = link.Open(slaveNode)
if err != nil {
glog.Fatal(err)
}
}
link.setLink(link)
link.setMaster(master)
link.setSlave(slave)
link.setQuoteChar(link.getQuoteCharLeft(), link.getQuoteCharRight())
return link, nil
}
// 设置master链接对象
func (l *dbLink) setMaster(master *sql.DB) {
l.master = master
}
// 设置slave链接对象
func (l *dbLink) setSlave(slave *sql.DB) {
l.slave = slave
}
// 设置当前数据库类型引用字符
func (l *dbLink) setQuoteChar(left string, right string) {
l.charl = left
l.charr = right
}
// 设置挡脸操作的link接口
func (l *dbLink) setLink(link Link) {
l.link = link
}

337
g/database/gdb/db_base.go Normal file
View File

@ -0,0 +1,337 @@
package gdb
import (
"fmt"
"errors"
"strings"
"database/sql"
"g/os/glog"
)
// 关闭链接
func (l *dbLink) Close() error {
if l.master != nil {
err := l.master.Close()
if (err == nil) {
l.master = nil
} else {
glog.Fatal(err)
return err
}
}
if l.slave != nil {
err := l.slave.Close()
if (err == nil) {
l.slave = nil
} else {
glog.Fatal(err)
return err
}
}
return nil
}
// 数据库sql查询操作主要执行查询
func (l *dbLink) Query(q string, args ...interface{}) (*sql.Rows, error) {
p := l.link.handleSqlBeforeExec(&q)
rows, err := l.slave.Query(*p, args ...)
err = l.formatError(err, p, args...)
if (err == nil) {
return rows, nil
}
return nil, err
}
// 执行一条sql并返回执行情况主要用于非查询操作
func (l *dbLink) Exec(q string, args ...interface{}) (sql.Result, error) {
//fmt.Println(q)
//fmt.Println(args)
p := l.link.handleSqlBeforeExec(&q)
r, err := l.master.Exec(*p, args ...)
err = l.formatError(err, p, args...)
return r, err
}
// 格式化错误信息
func (l *dbLink) formatError(err error, q *string, args ...interface{}) error {
if err != nil {
errstr := fmt.Sprintf("DB ERROR: %s\n", err.Error())
errstr += fmt.Sprintf("DB QUERY: %s\n", *q)
if len(args) > 0 {
errstr += fmt.Sprintf("DB PARAM: %v\n", args)
}
err = errors.New(errstr)
}
return err
}
// 数据库查询,获取查询结果集,以列表结构返回
func (l *dbLink) GetAll(q string, args ...interface{}) (*List, error) {
// 执行sql
rows, err := l.Query(q, args ...)
if err != nil || rows == nil {
return nil, err
}
// 列名称列表
columns, err := rows.Columns()
if err != nil {
return nil, err
}
// 返回结构组装
values := make([]sql.RawBytes, len(columns))
scanArgs := make([]interface{}, len(values))
var list List
for i := range values {
scanArgs[i] = &values[i]
}
for rows.Next() {
err = rows.Scan(scanArgs...)
if err != nil {
return &list, err
}
row := make(Map)
for i, col := range values {
row[columns[i]] = string(col)
}
list = append(list, row)
}
return &list, nil
}
// 数据库查询,获取查询结果集,以关联数组结构返回
func (l *dbLink) GetOne(q string, args ...interface{}) (*Map, error) {
list, err := l.GetAll(q, args ...)
if err != nil {
return nil, err
}
return &(*list)[0], nil
}
// 数据库查询,获取查询字段值
func (l *dbLink) GetValue(q string, args ...interface{}) (interface{}, error) {
one, err := l.GetOne(q, args ...)
if err != nil {
return "", err
}
for _, v := range *one {
return v, nil
}
return "", nil
}
// sql预处理执行完成后调用返回值sql.Stmt.Exec完成sql操作
// 记得调用sql.Stmt.Close关闭操作对象
func (l *dbLink) Prepare(q string) (*sql.Stmt, error) {
return l.master.Prepare(q)
}
// ping一下判断或保持数据库链接(master)
func (l *dbLink) PingMaster() error {
err := l.master.Ping();
return err
}
// ping一下判断或保持数据库链接(slave)
func (l *dbLink) PingSlave() error {
err := l.slave.Ping();
return err
}
// 设置数据库连接池中空闲链接的大小
func (l *dbLink) SetMaxIdleConns(n int) {
l.master.SetMaxIdleConns(n);
}
// 设置数据库连接池最大打开的链接数量
func (l *dbLink) SetMaxOpenConns(n int) {
l.master.SetMaxOpenConns(n);
}
// 事务操作,开启,会返回一个底层的事务操作对象链接如需要嵌套事务,那么可以使用该对象,否则请忽略
func (l *dbLink) Begin() (*sql.Tx, error) {
tx, err := l.master.Begin()
if err == nil {
l.transaction = tx
}
return tx, err
}
// 事务操作,提交
func (l *dbLink) Commit() error {
if l.transaction == nil {
return errors.New("transaction not start")
}
err := l.transaction.Commit()
return err
}
// 事务操作,回滚
func (l *dbLink) Rollback() error {
if l.transaction == nil {
return errors.New("transaction not start")
}
err := l.transaction.Rollback()
return err
}
// 根据insert选项获得操作名称
func (l *dbLink) getInsertOperationByOption(option uint8) string {
oper := "INSERT"
switch option {
case OPTION_INSERT:
case OPTION_REPLACE:
oper = "REPLACE"
case OPTION_SAVE:
case OPTION_IGNORE:
oper = "INSERT IGNORE"
}
return oper
}
// insert、replace, save ignore操作
// 0: insert: 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
// 1: replace: 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
// 2: save: 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
// 3: ignore: 如果数据存在(主键或者唯一索引),那么什么也不做
func (l *dbLink) insert(table string, data *Map, option uint8) (sql.Result, error) {
var keys []string
var values []string
var params []interface{}
for k, v := range *data {
keys = append(keys, l.charl + k + l.charr)
values = append(values, "?")
params = append(params, v)
}
operation := l.getInsertOperationByOption(option)
updatestr := ""
if option == OPTION_SAVE {
var updates []string
for k, _ := range *data {
updates = append(updates, fmt.Sprintf("%s%s%s=VALUES(%s)", l.charl, k, l.charr, k))
}
updatestr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
}
return l.Exec(
fmt.Sprintf("%s INTO %s%s%s(%s) VALUES(%s) %s",
operation, l.charl, table, l.charr, strings.Join(keys, ","), strings.Join(values, ","), updatestr), params...
)
}
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
func (l *dbLink) Insert(table string, data *Map) (sql.Result, error) {
return l.link.insert(table, data, OPTION_INSERT)
}
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
func (l *dbLink) Replace(table string, data *Map) (sql.Result, error) {
return l.link.insert(table, data, OPTION_REPLACE)
}
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
func (l *dbLink) Save(table string, data *Map) (sql.Result, error) {
return l.link.insert(table, data, OPTION_SAVE)
}
// 批量写入数据
func (l *dbLink) batchInsert(table string, list *List, batch int, option uint8) error {
var keys []string
var values []string
var bvalues []string
var params []interface{}
var size int = len(*list)
// 判断长度
if size < 1 {
return errors.New("empty data list")
}
// 首先获取字段名称及记录长度
for k, _ := range (*list)[0] {
keys = append(keys, k)
values = append(values, "?")
}
var kstr = l.charl + strings.Join(keys, l.charl + "," + l.charr) + l.charr
// 操作判断
operation := l.getInsertOperationByOption(option)
updatestr := ""
if option == OPTION_SAVE {
var updates []string
for _, k := range keys {
updates = append(updates, fmt.Sprintf("%s=VALUES(%s)", l.charl, k, l.charr, k))
}
updatestr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
}
// 构造批量写入数据格式(注意map的遍历是无序的)
for i := 0; i < size; i++ {
for _, k := range keys {
params = append(params, (*list)[i][k])
}
bvalues = append(bvalues, "(" + strings.Join(values, ",") + ")")
if len(bvalues) == batch {
_, err := l.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s", operation, l.charl, table, l.charr, kstr, strings.Join(bvalues, ","), updatestr), params...)
if err != nil {
return err
}
bvalues = bvalues[:0]
}
}
// 处理最后不构成指定批量的数据
if (len(bvalues) > 0) {
_, err := l.Exec(fmt.Sprintf("%s INTO %s%s%s(%s) VALUES%s %s", operation, l.charl, table, l.charr, kstr, strings.Join(bvalues, ","), updatestr), params...)
if err != nil {
return err
}
}
return nil
}
// CURD操作:批量数据指定批次量写入
func (l *dbLink) BatchInsert(table string, list *List, batch int) error {
return l.link.batchInsert(table, list, batch, OPTION_INSERT)
}
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
func (l *dbLink) BatchReplace(table string, list *List, batch int) error {
return l.link.batchInsert(table, list, batch, OPTION_REPLACE)
}
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
func (l *dbLink) BatchSave(table string, list *List, batch int) error {
return l.link.batchInsert(table, list, batch, OPTION_SAVE)
}
// CURD操作:数据更新统一采用sql预处理
// data参数支持字符串或者关联数组类型内部会自行做判断处理
func (l *dbLink) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
var params []interface{}
var updates string
switch data.(type) {
case string:
updates = data.(string)
case *Map:
var keys []string
for k, v := range *data.(*Map) {
keys = append(keys, fmt.Sprintf("%s%s%s=?", l.charl, k, l.charr))
params = append(params, v)
}
updates = strings.Join(keys, ",")
default:
return nil, errors.New("invalid data type for 'data' field, string or *Map expected")
}
for _, v := range args {
if r, ok := v.(string); ok {
params = append(params, r)
} else if r, ok := v.(int); ok {
params = append(params, string(r))
} else {
}
}
return l.Exec(fmt.Sprintf("UPDATE %s%s%s SET %s WHERE %s", l.charl, table, l.charr, updates, condition), params...)
}
// CURD操作:删除数据
func (l *dbLink) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) {
return l.Exec(fmt.Sprintf("DELETE FROM %s WHERE %s", l.charl, table, l.charr, condition), args...)
}

236
g/database/gdb/db_linkop.go Normal file
View File

@ -0,0 +1,236 @@
package gdb
import (
"fmt"
"database/sql"
"errors"
_ "github.com/go-sql-driver/mysql"
)
// gf的数据库操作支持普通方法操作及链式操作两种方式本文件是链式操作的封装提供非常简便的CURD方法
// 数据库链式操作对象
type gLinkOp struct {
link Link
tables string
fields string
condition string
conditionArgs []interface{}
groupby string
orderby string
start int
limit int
data interface{}
dataList *List
batch int
}
// 链式操作,数据表字段,可支持多个表,以半角逗号连接
func (l *dbLink) Table(tables string) (*gLinkOp) {
return &gLinkOp{
link : l.link,
tables: tables,
}
}
// 链式操作,左联表
func (op *gLinkOp) LeftJoin(joinTable string, on string) (*gLinkOp) {
op.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", joinTable, on)
return op
}
// 链式操作,右联表
func (op *gLinkOp) RightJoin(joinTable string, on string) (*gLinkOp) {
op.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", joinTable, on)
return op
}
// 链式操作,内联表
func (op *gLinkOp) InnerJoin(joinTable string, on string) (*gLinkOp) {
op.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", joinTable, on)
return op
}
// 链式操作,查询字段
func (op *gLinkOp) Fields(fields string) (*gLinkOp) {
op.fields = fields
return op
}
// 链式操作consition
func (op *gLinkOp) Condition(condition string, args...interface{}) (*gLinkOp) {
op.condition = condition
op.conditionArgs = args
return op
}
// 链式操作group by
func (op *gLinkOp) GroupBy(groupby string) (*gLinkOp) {
op.groupby = groupby
return op
}
// 链式操作order by
func (op *gLinkOp) OrderBy(orderby string) (*gLinkOp) {
op.orderby = orderby
return op
}
// 链式操作limit
func (op *gLinkOp) Limit(start int, limit int) (*gLinkOp) {
op.start = start
op.limit = limit
return op
}
// 链式操作,操作数据记录项
func (op *gLinkOp) Data(data interface{}) (*gLinkOp) {
op.data = data
return op
}
// 链式操作,操作数据记录项列表
func (op *gLinkOp) List(list *List) (*gLinkOp) {
op.dataList = list
return op
}
// 链式操作, CURD - Insert
func (op *gLinkOp) Insert() (sql.Result, error) {
if op.data == nil {
return nil, errors.New("inserting into table with empty data")
}
if d, ok := op.data.(*Map); ok {
return op.link.Insert(op.tables, d)
}
return nil, errors.New("inserting into table with invalid data type")
}
// 链式操作, CURD - Replace
func (op *gLinkOp) Replace() (sql.Result, error) {
if op.data == nil {
return nil, errors.New("replacing into table with empty data")
}
if d, ok := op.data.(*Map); ok {
return op.link.Insert(op.tables, d)
}
return nil, errors.New("replacing into table with invalid data type")
}
// 链式操作, CURD - Save
func (op *gLinkOp) Save() (sql.Result, error) {
if op.data == nil {
return nil, errors.New("saving into table with empty data")
}
if d, ok := op.data.(*Map); ok {
return op.link.Insert(op.tables, d)
}
return nil, errors.New("saving into table with invalid data type")
}
// 设置批处理的大小
func (op *gLinkOp) Batch(batch int) *gLinkOp {
op.batch = batch
return op
}
// 链式操作, CURD - BatchInsert
func (op *gLinkOp) BatchInsert() error {
if op.dataList == nil || len(*op.dataList) < 1 {
return errors.New("batch inserting into table with empty data list")
}
batch := 10
if op.batch > 0 {
batch = op.batch
}
return op.link.BatchInsert(op.tables, op.dataList, batch)
}
// 链式操作, CURD - BatchReplace
func (op *gLinkOp) BatchReplace() error {
if op.dataList == nil || len(*op.dataList) < 1 {
return errors.New("batch replacing into table with empty data list")
}
batch := 10
if op.batch > 0 {
batch = op.batch
}
return op.link.BatchReplace(op.tables, op.dataList, batch)
}
// 链式操作, CURD - BatchSave
func (op *gLinkOp) BatchSave() error {
if op.dataList == nil || len(*op.dataList) < 1 {
return errors.New("batch saving into table with empty data list")
}
batch := 10
if op.batch > 0 {
batch = op.batch
}
return op.link.BatchSave(op.tables, op.dataList, batch)
}
// 链式操作, CURD - Update
func (op *gLinkOp) Update() (sql.Result, error) {
if op.data == nil {
return nil, errors.New("updating table with empty data")
}
return op.link.Update(op.tables, op.data, op.condition, op.conditionArgs ...)
}
// 链式操作, CURD - Delete
func (op *gLinkOp) Delete() (sql.Result, error) {
if op.condition == "" {
return nil, errors.New("condition is required while deleting")
}
return op.link.Delete(op.tables, op.condition, op.conditionArgs...)
}
// 链式操作select
func (op *gLinkOp) Select() (*List, error) {
if op.fields == "" {
op.fields = "*"
}
s := fmt.Sprintf("SELECT %s FROM %s", op.fields, op.tables)
if op.condition != "" {
s += " WHERE " + op.condition
}
if op.groupby != "" {
s += " GROUP BY " + op.groupby
}
if op.orderby != "" {
s += " ORDER BY " + op.orderby
}
if op.limit != 0 {
s += fmt.Sprintf(" LIMIT %d, %d", op.start, op.limit)
}
return op.link.GetAll(s, op.conditionArgs...)
}
// 链式操作,查询所有记录
func (op *gLinkOp) All() (*List, error) {
return op.Select()
}
// 链式操作,查询单条记录
func (op *gLinkOp) One() (*Map, error) {
list, err := op.All()
if err != nil {
return nil, err
}
return &(*list)[0], nil
}
// 链式操作,查询字段值
func (op *gLinkOp) Value() (interface{}, error) {
one, err := op.One()
if err != nil {
return "", err
}
for _, v := range *one {
return v, nil
}
return "", nil
}

View File

@ -0,0 +1,42 @@
package gdb
import (
"database/sql"
"fmt"
"g/os/glog"
)
// 数据库链接对象
type mysqlLink struct {
dbLink
}
// 创建SQL操作对象内部采用了lazy link处理
func (l *mysqlLink) Open (c *ConfigNode) (*sql.DB, error) {
var dbsource string
if c.Linkinfo != "" {
dbsource = c.Linkinfo
} else {
dbsource = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", c.User, c.Pass, c.Host, c.Port, c.Name)
}
db, err := sql.Open("mysql", dbsource)
if err != nil {
glog.Fatal(err)
}
return db, err
}
// 获得关键字操作符 - 左
func (l *mysqlLink) getQuoteCharLeft () string {
return "`"
}
// 获得关键字操作符 - 右
func (l *mysqlLink) getQuoteCharRight () string {
return "`"
}
// 在执行sql之前对sql进行进一步处理
func (l *mysqlLink) handleSqlBeforeExec(q *string) *string {
return q
}

View File

@ -0,0 +1,53 @@
package gdb
import (
"database/sql"
"fmt"
"regexp"
"g/os/glog"
)
// postgresql的适配
// @todo 需要完善replace和save的操作覆盖
// 数据库链接对象
type pgsqlLink struct {
dbLink
}
// 创建SQL操作对象内部采用了lazy link处理
func (l *pgsqlLink) Open (c *ConfigNode) (*sql.DB, error) {
var dbsource string
if c.Linkinfo != "" {
dbsource = c.Linkinfo
} else {
dbsource = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s", c.User, c.Pass, c.Host, c.Port, c.Name)
}
db, err := sql.Open("postgres", dbsource)
if err != nil {
glog.Fatal(err)
}
return db, err
}
// 获得关键字操作符 - 左
func (l *pgsqlLink) getQuoteCharLeft () string {
return "\""
}
// 获得关键字操作符 - 右
func (l *pgsqlLink) getQuoteCharRight () string {
return "\""
}
// 在执行sql之前对sql进行进一步处理
func (l *pgsqlLink) handleSqlBeforeExec(q *string) *string {
reg := regexp.MustCompile("\\?")
index := 0
str := reg.ReplaceAllStringFunc(*q, func (s string) string {
index ++
return fmt.Sprintf("$%d", index)
})
return &str
}

View File

@ -0,0 +1,16 @@
package gbase64
import (
"encoding/base64"
)
// base64 encode
func Encode(str string) string {
return base64.StdEncoding.EncodeToString([]byte(str))
}
// base64 decode
func Decode(str string) (string, error) {
s, e := base64.StdEncoding.DecodeString(str)
return string(s), e
}

View File

@ -0,0 +1,197 @@
// 二进制及byte操作管理包
package gbinary
import (
"bytes"
"encoding/binary"
"math"
)
// 二进制位(0|1)
type Bit uint8
// (通用,效率较低)二进制打包
func Encode(vs ...interface{}) ([]byte, error) {
buf := new(bytes.Buffer)
for i := 0; i < len(vs); i++ {
err := binary.Write(buf, binary.LittleEndian, vs[i])
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
// (通用,效率较低)二进制解包,注意第二个参数之后的变量是变量的指针地址
func Decode(b []byte, vs ...interface{}) error {
buf := bytes.NewBuffer(b)
for i := 0; i < len(vs); i++ {
err := binary.Read(buf, binary.LittleEndian, vs[i])
if err != nil {
return err
}
}
return nil
}
func EncodeString(s string) []byte {
return []byte(s)
}
func DecodeToString(b []byte) string {
return string(b)
}
func EncodeInt8(i int8) []byte {
return []byte{byte(i)}
}
func EncodeUint8(i uint8) []byte {
return []byte{byte(i)}
}
func EncodeInt16(i int16) []byte {
bytes := make([]byte, 2)
binary.LittleEndian.PutUint16(bytes, uint16(i))
return bytes
}
func EncodeUint16(i uint16) []byte {
bytes := make([]byte, 2)
binary.LittleEndian.PutUint16(bytes, i)
return bytes
}
func EncodeInt32(i int32) []byte {
bytes := make([]byte, 4)
binary.LittleEndian.PutUint32(bytes, uint32(i))
return bytes
}
func EncodeUint32(i uint32) []byte {
bytes := make([]byte, 4)
binary.LittleEndian.PutUint32(bytes, i)
return bytes
}
func EncodeInt64(i int64) []byte {
bytes := make([]byte, 8)
binary.LittleEndian.PutUint64(bytes, uint64(i))
return bytes
}
func EncodeUint64(i uint64) []byte {
bytes := make([]byte, 8)
binary.LittleEndian.PutUint64(bytes, i)
return bytes
}
func EncodeFloat32(f float32) []byte {
bits := math.Float32bits(f)
bytes := make([]byte, 4)
binary.LittleEndian.PutUint32(bytes, bits)
return bytes
}
func EncodeFloat64(f float64) []byte {
bits := math.Float64bits(f)
bytes := make([]byte, 8)
binary.LittleEndian.PutUint64(bytes, bits)
return bytes
}
// 当b位数不够时进行高位补0
func fillUpSize(b []byte, l int) []byte {
c := make([]byte, 0)
c = append(c, b...)
for i := 0; i <= l - len(b); i++ {
c = append(c, 0x00)
}
return c
}
func DecodeToInt8(b []byte) int8 {
return int8(b[0])
}
func DecodeToUint8(b []byte) uint8 {
return uint8(b[0])
}
func DecodeToInt16(b []byte) int16 {
return int16(binary.LittleEndian.Uint16(fillUpSize(b, 2)))
}
func DecodeToUint16(b []byte) uint16 {
return binary.LittleEndian.Uint16(fillUpSize(b, 2))
}
func DecodeToInt32(b []byte) int32 {
return int32(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
}
func DecodeToUint32(b []byte) uint32 {
return binary.LittleEndian.Uint32(fillUpSize(b, 4))
}
func DecodeToInt64(b []byte) int64 {
return int64(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
}
func DecodeToUint64(b []byte) uint64 {
return binary.LittleEndian.Uint64(fillUpSize(b, 8))
}
func DecodeToFloat32(b []byte) float32 {
return math.Float32frombits(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
}
func DecodeToFloat64(b []byte) float64 {
return math.Float64frombits(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
}
// 将ui按位合并到bits数组中并占length长度位(注意uis数组中存放的是二进制的0|1数字)
func EncodeBits(bits []Bit, ui uint, l int) []Bit {
a := make([]Bit, l)
for i := l - 1; i >= 0; i-- {
a[i] = Bit(ui & 1)
ui >>= 1
}
if bits != nil {
return append(bits, a...)
} else {
return a
}
}
// 将bits转换为[]byte从左至右进行编码不足1 byte按0往末尾补充
func EncodeBitsToBytes(bits []Bit) []byte {
if len(bits)%8 != 0 {
for i := 0; i < len(bits)%8; i++ {
bits = append(bits, 0)
}
}
b := make([]byte, 0)
for i := 0; i < len(bits); i += 8 {
b = append(b, byte(DecodeBits(bits[i : i + 8])))
}
return b
}
// 从ui字位数组中解析为uint
func DecodeBits(bits []Bit) uint {
ui := uint(0)
for _, i := range bits {
ui = ui << 1 | uint(i)
}
return ui
}
// 解析[]byte为字位数组[]uint8
func DecodeBytesToBits(bs []byte) []Bit {
bits := make([]Bit, 0)
for _, b := range bs {
bits = EncodeBits(bits, uint(b), 8)
}
return bits
}

View File

@ -0,0 +1,34 @@
package gcompress
import (
"bytes"
"compress/zlib"
"io"
)
// 进行zlib压缩
func Zlib(data []byte) []byte {
if data == nil || len(data) < 13 {
return data
}
var in bytes.Buffer
w := zlib.NewWriter(&in)
w.Write(data)
w.Close()
return in.Bytes()
}
// 进行zlib解压缩
func UnZlib(data []byte) []byte {
if data == nil || len(data) < 13 {
return data
}
b := bytes.NewReader(data)
var out bytes.Buffer
r, err := zlib.NewReader(b)
if err != nil {
return nil
}
io.Copy(&out, r)
return out.Bytes()
}

View File

@ -0,0 +1,13 @@
package gcrc32
import (
"hash/crc32"
)
func EncodeString(v string) uint32 {
return crc32.ChecksumIEEE([]byte(v))
}
func EncodeBytes(v []byte) uint32 {
return crc32.ChecksumIEEE(v)
}

191
g/encoding/ghash/ghash.go Normal file
View File

@ -0,0 +1,191 @@
// 封装常用的hash函数
package ghash
// BKDR Hash Function
func BKDRHash(str []byte) uint32 {
var seed uint32 = 131; // 31 131 1313 13131 131313 etc..
var hash uint32 = 0;
for i := 0; i < len(str); i++ {
hash = hash * seed + uint32(str[i])
}
return hash
}
// BKDR Hash Function 64
func BKDRHash64(str []byte) uint64 {
var seed uint64 = 131; // 31 131 1313 13131 131313 etc..
var hash uint64 = 0;
for i := 0; i < len(str); i++ {
hash = hash * seed + uint64(str[i])
}
return hash
}
// SDBM Hash
func SDBMHash(str []byte) uint32 {
var hash uint32 = 0;
for i := 0; i < len(str); i++ {
// equivalent to: hash = 65599*hash + uint32(str[i]);
hash = uint32(str[i]) + (hash << 6) + (hash << 16) - hash;
}
return hash
}
// SDBM Hash 64
func SDBMHash64(str []byte) uint64 {
var hash uint64 = 0;
for i := 0; i < len(str); i++ {
// equivalent to: hash = 65599*hash + uint32(str[i]);
hash = uint64(str[i]) + (hash << 6) + (hash << 16) - hash;
}
return hash
}
// RS Hash Function
func RSHash(str []byte) uint32 {
var b uint32 = 378551;
var a uint32 = 63689;
var hash uint32 = 0;
for i := 0; i < len(str); i++ {
hash = hash * a + uint32(str[i]);
a *= b;
}
return hash
}
// RS Hash Function 64
func RSHash64(str []byte) uint64 {
var b uint64 = 378551;
var a uint64 = 63689;
var hash uint64 = 0;
for i := 0; i < len(str); i++ {
hash = hash * a + uint64(str[i]);
a *= b;
}
return hash
}
// JS Hash Function
func JSHash(str []byte) uint32 {
var hash uint32 = 1315423911;
for i := 0; i < len(str); i++ {
hash ^= ((hash << 5) + uint32(str[i]) + (hash >> 2));
}
return hash
}
// JS Hash Function 64
func JSHash64(str []byte) uint64 {
var hash uint64 = 1315423911;
for i := 0; i < len(str); i++ {
hash ^= ((hash << 5) + uint64(str[i]) + (hash >> 2));
}
return hash
}
// P. J. Weinberger Hash Function
func PJWHash(str []byte) uint32 {
var BitsInUnignedInt uint32 = (4 * 8);
var ThreeQuarters uint32 = ((BitsInUnignedInt * 3) / 4);
var OneEighth uint32 = (BitsInUnignedInt / 8);
var HighBits uint32 = (0xFFFFFFFF) << (BitsInUnignedInt - OneEighth);
var hash uint32 = 0;
var test uint32 = 0;
for i := 0; i < len(str); i++ {
hash = (hash << OneEighth) + uint32(str[i]);
if test = hash & HighBits; test != 0 {
hash = ((hash ^ (test >> ThreeQuarters)) & (^HighBits + 1));
}
}
return hash
}
// P. J. Weinberger Hash Function 64
func PJWHash64(str []byte) uint64 {
var BitsInUnignedInt uint64 = (4 * 8);
var ThreeQuarters uint64 = ((BitsInUnignedInt * 3) / 4);
var OneEighth uint64 = (BitsInUnignedInt / 8);
var HighBits uint64 = (0xFFFFFFFFFFFFFFFF) << (BitsInUnignedInt - OneEighth);
var hash uint64 = 0;
var test uint64 = 0;
for i := 0; i < len(str); i++ {
hash = (hash << OneEighth) + uint64(str[i]);
if test = hash & HighBits; test != 0 {
hash = ((hash ^ (test >> ThreeQuarters)) & (^HighBits + 1));
}
}
return hash
}
// ELF Hash Function
func ELFHash(str []byte) uint32 {
var hash uint32 = 0;
var x uint32 = 0;
for i := 0; i < len(str); i++ {
hash = (hash << 4) + uint32(str[i]);
if x = hash & 0xF0000000; x != 0 {
hash ^= (x >> 24);
hash &= ^x + 1;
}
}
return hash
}
// ELF Hash Function 64
func ELFHash64(str []byte) uint64 {
var hash uint64 = 0;
var x uint64 = 0;
for i := 0; i < len(str); i++ {
hash = (hash << 4) + uint64(str[i]);
if x = hash & 0xF000000000000000; x != 0 {
hash ^= (x >> 24);
hash &= ^x + 1;
}
}
return hash
}
// DJB Hash Function
func DJBHash(str []byte) uint32 {
var hash uint32 = 5381;
for i := 0; i < len(str); i++ {
hash += (hash << 5) + uint32(str[i]);
}
return hash
}
// DJB Hash Function 64
func DJBHash64(str []byte) uint64 {
var hash uint64 = 5381;
for i := 0; i < len(str); i++ {
hash += (hash << 5) + uint64(str[i]);
}
return hash
}
// AP Hash Function
func APHash(str []byte) uint32 {
var hash uint32 = 0;
for i := 0; i < len(str); i++ {
if ((i & 1) == 0) {
hash ^= ((hash << 7) ^ uint32(str[i]) ^ (hash >> 3));
} else {
hash ^= (^((hash << 11) ^ uint32(str[i]) ^ (hash >> 5)) + 1);
}
}
return hash
}
// AP Hash Function 64
func APHash64(str []byte) uint64 {
var hash uint64 = 0;
for i := 0; i < len(str); i++ {
if ((i & 1) == 0) {
hash ^= ((hash << 7) ^ uint64(str[i]) ^ (hash >> 3));
} else {
hash ^= (^((hash << 11) ^ uint64(str[i]) ^ (hash >> 5)) + 1);
}
}
return hash
}

25
g/encoding/ghtml/ghtml.go Normal file
View File

@ -0,0 +1,25 @@
package ghtml
import "strings"
// 将html中的特殊标签转换为html转义标签
func SpecialChars(s string) string {
return strings.NewReplacer(
"&", "&amp;",
"<", "&lt;",
">", "&gt;",
`"`, "&#34;",
"'", "&#39;",
).Replace(s)
}
// 将html转义标签还原为html特殊标签
func SpecialCharsDecode(s string) string {
return strings.NewReplacer(
"&amp;", "&",
"&lt;", "<",
"&gt;", ">",
"&#34;", `"`,
"&#39;", "'",
).Replace(s)
}

View File

@ -0,0 +1,436 @@
package internal
import (
"fmt"
"errors"
"strings"
"strconv"
)
// 这是一个使用go进行json语法解析的解析器效率没有官方的json解析高仅作学习参考
const (
gJSON_CHAR_BRACE_LEFT = rune('{')
gJSON_CHAR_BRACE_RIGHT = rune('}')
gJSON_CHAR_BRACKET_LEFT = rune('[')
gJSON_CHAR_BRACKET_RIGHT = rune(']')
gJSON_CHAR_QUOTATION = rune('\\')
gJSON_CHAR_COMMA = rune(',')
gJSON_CHAR_COLON = rune(':')
gJSON_CHAR_DOUBLE_QUOTE_MARK = rune('"')
)
const (
gJSON_TOKEN_BRACE_LEFT = rune('{')
gJSON_TOKEN_BRACE_RIGHT = rune('}')
gJSON_TOKEN_BRACKET_LEFT = rune('[')
gJSON_TOKEN_BRACKET_RIGHT = rune(']')
gJSON_TOKEN_COMMA = rune(',')
gJSON_TOKEN_COLON = rune(':')
gJSON_TOKEN_STRING = rune('"')
gJSON_TOKEN_NUMBER = rune('0')
)
// json关联数组(哈希表)
type JsonMap map[string]interface{}
// json索引数组(普通数组从0开始索引)
type JsonArray []interface{}
// JSON数据对象
type gJsonNode struct {
m JsonMap
a JsonArray
}
// JSON语义token
type gJsonToken struct {
token []rune // token字符串
tokenType rune // token类型
tokenindex int // token在原始字符串中的索引位置
}
// JSON解析结构对象
type gJsonParser struct {
content []rune // 需要解析json字符串(通过string转换为[]rune)
tokens []gJsonToken // 存放解析content后的json token数组
root *gJsonNode // json根节点
pointer *gJsonNode // 指向当前正在解析的json节点
}
// 解析json字符串
func Decode(j *string) (*gJsonParser, error) {
p := &gJsonParser{content:[]rune(*j)}
err := p.parse()
if err == nil {
return p, err
} else {
return nil, err
}
}
// 判断所给字符串是否为数字
func isNumeric(s string) bool {
for i :=0; i < len(s); i++ {
if s[i] < byte('0') || s[i] > byte('9') {
return false
}
}
return true
}
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (p *gJsonParser) GetMap(pattern string) JsonMap {
result := p.Get(pattern)
if result != nil {
if r, ok := result.(JsonMap); ok {
return r
}
}
return nil
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (p *gJsonParser) GetArray(pattern string) JsonArray {
result := p.Get(pattern)
if result != nil {
if r, ok := result.(JsonArray); ok {
return r
}
}
return nil
}
// 根据约定字符串方式访问json解析数据参数形如 "items.name.first", "list.0"
// 返回的结果类型的interface{},因此需要自己做类型转换
// 如果找不到对应节点的数据返回nil
func (p *gJsonParser) Get(pattern string) interface{} {
var result interface{}
pointer := p.root
array := strings.Split(pattern, ".")
length := len(array)
for i:= 0; i < length; i++ {
// 优先判断数组
if isNumeric(array[i]) {
n, err := strconv.Atoi(array[i])
if err == nil && len(pointer.a) > n {
if i == length - 1 {
result = pointer.a[n]
break;
} else {
if p, ok := pointer.a[n].(*gJsonNode); ok {
pointer = p
continue
}
}
}
}
// 其次判断哈希表如果一个键在数组及map中均不存在直接返回nil
if v, ok := pointer.m[array[i]]; ok {
if i == length - 1 {
result = v
} else {
if p, ok := v.(*gJsonNode); ok {
pointer = p
continue
}
}
} else {
return nil
}
}
// 处理结果如果是gJsonNode类型那么需要做转换
if r, ok := result.(*gJsonNode); ok {
if len(r.m) < 1 {
return r.a
} else {
return r.m
}
}
return result
}
// 遍历json字符串数组并且判断转义
func (p *gJsonParser) getNextChar(c rune, f int) int {
for i := f + 1; i < len(p.content); i++ {
if p.content[i] == c {
if i > 0 && p.content[i - 1] != gJSON_CHAR_QUOTATION {
return i
}
} else {
switch p.content[i] {
case gJSON_CHAR_DOUBLE_QUOTE_MARK:
r := p.getNextChar(gJSON_CHAR_DOUBLE_QUOTE_MARK, i)
if r > 0 {
i = r
}
}
}
}
return 0
}
// 判断字符是否为数字
func (p *gJsonParser) isCharNumber(c rune) bool {
if c >= rune('0') && c <= rune('9') {
return true
}
return false
}
// 按照json语法对保存的字符串进行解析
func (p *gJsonParser) parse() error {
// 首先将字符串解析成token进行保存
for i := 0; i < len(p.content); i++ {
if p.isCharNumber(p.content[i]) {
j := i + 1
for ; j < len(p.content); j++ {
if !p.isCharNumber(p.content[j]) {
break;
}
}
p.tokens = append(p.tokens, gJsonToken {
token: p.content[i:j],
tokenType: gJSON_TOKEN_NUMBER,
tokenindex: i,
})
i = j - 1
} else {
switch p.content[i] {
case gJSON_CHAR_DOUBLE_QUOTE_MARK:
r := p.getNextChar(gJSON_CHAR_DOUBLE_QUOTE_MARK, i)
if r > 0 {
// 注意这里需要去掉字符串两边的双引号
p.tokens = append(p.tokens, gJsonToken {
token: p.content[i+1:r],
tokenType: gJSON_TOKEN_STRING,
tokenindex: i,
})
i = r
}
case gJSON_CHAR_COLON:
p.tokens = append(p.tokens, gJsonToken{token: p.content[i:i+1], tokenType: gJSON_TOKEN_COLON, tokenindex: i})
case gJSON_CHAR_COMMA:
p.tokens = append(p.tokens, gJsonToken{token: p.content[i:i+1], tokenType: gJSON_TOKEN_COMMA, tokenindex: i})
case gJSON_CHAR_BRACE_LEFT:
p.tokens = append(p.tokens, gJsonToken{token: p.content[i:i+1], tokenType: gJSON_TOKEN_BRACE_LEFT, tokenindex: i})
case gJSON_CHAR_BRACE_RIGHT:
p.tokens = append(p.tokens, gJsonToken{token: p.content[i:i+1], tokenType: gJSON_TOKEN_BRACE_RIGHT, tokenindex: i})
case gJSON_CHAR_BRACKET_LEFT:
p.tokens = append(p.tokens, gJsonToken{token: p.content[i:i+1], tokenType: gJSON_TOKEN_BRACKET_LEFT, tokenindex: i})
case gJSON_CHAR_BRACKET_RIGHT:
p.tokens = append(p.tokens, gJsonToken{token: p.content[i:i+1], tokenType: gJSON_TOKEN_BRACKET_RIGHT, tokenindex: i})
default:
c := string(p.content[i])
if c != " " && c != "\r" && c != "\n" && c != "\t" {
return errors.New(fmt.Sprintf("json parse error: invalid char '%s' at index %d", c, i))
}
}
}
}
// 最后对解析后的token转换为go变量
return p.parseTokenNodeToVar(0, len(p.tokens) - 1)
}
// 获取json范围字符包含范围最右侧的索引位置
func (p *gJsonParser)getTokenBorderRightIndex(token rune, from int) int {
switch token {
case gJSON_TOKEN_BRACE_LEFT:
leftCount := 0
for i := from + 1; i < len(p.tokens); i++ {
if p.tokens[i].tokenType == gJSON_TOKEN_BRACE_LEFT {
leftCount ++
} else if p.tokens[i].tokenType == gJSON_TOKEN_BRACE_RIGHT {
if leftCount < 1 {
return i
} else {
leftCount--
}
}
}
case gJSON_CHAR_BRACKET_LEFT:
leftCount := 0
for i := from + 1; i < len(p.tokens); i++ {
if p.tokens[i].tokenType == gJSON_CHAR_BRACKET_LEFT {
leftCount ++
} else if p.tokens[i].tokenType == gJSON_CHAR_BRACKET_RIGHT {
if leftCount < 1 {
return i
} else {
leftCount--
}
}
}
}
return 0
}
// 将解析过后的json token转换为go变量
func (p *gJsonParser) parseTokenNodeToVar(left int, right int) error {
//fmt.Println("================================")
//for i := left; i <= right; i++ {
// fmt.Println(string(p.tokens[i].token))
//}
for i := left; i <= right; i++ {
//fmt.Println(string(p.tokens[i].token))
switch p.tokens[i].tokenType {
case gJSON_TOKEN_BRACE_LEFT:
fallthrough
case gJSON_TOKEN_BRACKET_LEFT:
node := newJsonNode()
// 判断根节点
if p.root == nil {
p.root = node
p.pointer = node
}
// 判断层级关系
borderRight := p.getTokenBorderRightIndex(p.tokens[i].tokenType, i)
if borderRight < 1 {
return errors.New(fmt.Sprintf("json parse error: unclosed tag '%s' at index %d", string(p.tokens[i].token), p.tokens[i].tokenindex))
}
if i > 1 && (
p.tokens[i-1].tokenType == gJSON_TOKEN_COLON &&
p.tokens[i-2].tokenType == gJSON_TOKEN_STRING) {
// json赋值操作
oldptr := p.pointer
k := string(p.tokens[i-2].token)
p.pointer.m[k] = node
p.pointer = node
err := p.parseTokenNodeToVar(i + 1, borderRight - 1)
if err != nil {
return err
} else {
i = borderRight
p.pointer = oldptr
}
} else if i > 0 && (
p.tokens[i-1].tokenType == gJSON_TOKEN_COMMA ||
p.tokens[i-1].tokenType == gJSON_TOKEN_BRACE_LEFT ||
p.tokens[i-1].tokenType == gJSON_TOKEN_BRACKET_LEFT) {
// json数组操作
oldptr := p.pointer
p.pointer.a = append(p.pointer.a, node)
p.pointer = node
err := p.parseTokenNodeToVar(i + 1, borderRight - 1)
if err != nil {
return err
} else {
i = borderRight
p.pointer = oldptr
}
} else {
// json层级关系
p.pointer = node
err := p.parseTokenNodeToVar(i + 1, borderRight - 1)
if err != nil {
return err
} else {
i = borderRight
}
}
case gJSON_TOKEN_STRING:
fallthrough
case gJSON_TOKEN_NUMBER:
if i > 0 && p.tokens[i-1].tokenType == gJSON_TOKEN_COLON {
k := string(p.tokens[i-2].token)
v := string(p.tokens[i].token)
p.pointer.m[k] = v
} else if p.tokens[i+1].tokenType != gJSON_TOKEN_COLON {
p.pointer.a = append(p.pointer.a, string(p.tokens[i].token))
}
case gJSON_TOKEN_COLON:
if i < 1 || (p.tokens[i-1].tokenType != gJSON_TOKEN_STRING) {
return errors.New(fmt.Sprintf("json parse error: invalid charactar '%s' at index %d", string(p.tokens[i].token), p.tokens[i].tokenindex))
}
case gJSON_TOKEN_COMMA:
if (p.tokens[i+1].tokenType != gJSON_TOKEN_STRING &&
p.tokens[i+1].tokenType != gJSON_TOKEN_NUMBER &&
p.tokens[i+1].tokenType != gJSON_TOKEN_BRACE_LEFT &&
p.tokens[i+1].tokenType != gJSON_TOKEN_BRACKET_LEFT) ||
(i < 1 || (
p.tokens[i-1].tokenType != gJSON_TOKEN_STRING &&
p.tokens[i-1].tokenType != gJSON_TOKEN_NUMBER &&
p.tokens[i-1].tokenType != gJSON_TOKEN_BRACE_RIGHT &&
p.tokens[i-1].tokenType != gJSON_TOKEN_BRACKET_RIGHT)) {
return errors.New(fmt.Sprintf("json parse error: invalid charactar '%s' at index %d", string(p.tokens[i].token), p.tokens[i].tokenindex))
}
}
}
return nil
}
// 打印出所有的token(测试用)
func (p *gJsonParser)printTokens() {
for _, v := range p.tokens {
fmt.Println(string(v.token))
}
}
// 格式化打印根节点
func (p *gJsonParser)Print() {
if len(p.root.m) > 0 {
fmt.Println("{")
} else {
fmt.Println("[")
}
p.printNode(p.pointer, "\t")
if len(p.root.m) > 0 {
fmt.Println("}")
} else {
fmt.Println("]")
}
}
// 格式化打印根节点
func (p *gJsonParser)printNode(n *gJsonNode, indent string) {
if len(n.m) > 0 {
for k, v := range n.m {
if t, ok := v.(*gJsonNode); ok {
if len(t.m) > 0 {
fmt.Printf("%v%v\t: {\n", indent, k)
p.printNode(t, indent + "\t")
fmt.Printf("%v}\n", indent)
} else {
fmt.Printf("%v%v\t: [\n", indent, k)
p.printNode(t, indent + "\t")
fmt.Printf("%v}\n", indent)
}
} else {
fmt.Printf("%v%v\t: %v\n", indent, k, v)
}
}
}
if len(n.a) > 0 {
for k, v := range n.a {
if t, ok := v.(*gJsonNode); ok {
if len(t.m) > 0 {
fmt.Printf("%v%v\t: {\n", indent, k)
p.printNode(t, indent + "\t")
fmt.Printf("%v}\n", indent)
} else {
fmt.Printf("%v%v\t: [\n", indent, k)
p.printNode(t, indent + "\t")
fmt.Printf("%v}\n", indent)
}
} else {
fmt.Printf("%v%v : %v\n", indent, k, v)
}
}
}
}
// 创建一个json数据对象
func newJsonNode() *gJsonNode {
return &gJsonNode {
m: make(map[string]interface{}),
a: make([]interface{}, 0),
}
}

216
g/encoding/gjson/json.go Normal file
View File

@ -0,0 +1,216 @@
package gjson
import (
"encoding/json"
"errors"
"strings"
"strconv"
"g/os/glog"
"fmt"
)
// json解析结果存放数组
type Json struct {
// 注意这是一个指针
value *interface{}
}
// 一个json变量
type JsonVar interface{}
// 编码go变量为json字符串并返回json字符串指针
func Encode (v interface{}) string {
s, err := json.Marshal(v)
if err != nil {
glog.Error("json marshaling failed: " + err.Error())
return ""
}
r := string(s)
return r
}
// 解码字符串为interface{}变量
func Decode (s string) interface{} {
var v interface{}
if DecodeTo(s, &v) == nil {
return v
}
return nil
}
// 解析json字符串为go变量注意第二个参数为指针
func DecodeTo (s string, v interface{}) error {
if err := json.Unmarshal([]byte(s), v); err != nil {
return errors.New("json unmarshaling failed: " + err.Error())
}
return nil
}
// 解析json字符串为gjson.Json对象并返回操作对象指针
func DecodeToJson (s string) *Json {
var result interface{}
if err := json.Unmarshal([]byte(s), &result); err != nil {
glog.Error("json unmarshaling failed: " + err.Error())
return nil
}
return &Json{ &result }
}
// 将变量转换为Json对象进行处理该变量至少应当是一个map或者array否者转换没有意义
func NewJson(v *interface{}) *Json {
return &Json{ v }
}
// 将指定的json内容转换为指定结构返回查找失败或者转换失败目标对象转换为nil
// 注意第二个参数需要给的是变量地址
func (p *Json) GetToVar(pattern string, v interface{}) error {
r := p.Get(pattern)
if r != nil {
return DecodeTo(Encode(r), v)
} else {
v = nil
}
return nil
}
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (p *Json) GetMap(pattern string) map[string]interface{} {
result := p.Get(pattern)
if result != nil {
if r, ok := result.(map[string]interface{}); ok {
return r
}
}
return nil
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (p *Json) GetArray(pattern string) []interface{} {
result := p.Get(pattern)
if result != nil {
if r, ok := result.([]interface{}); ok {
return r
}
}
return nil
}
// 返回指定json中的string
func (p *Json) GetString(pattern string) string {
result := p.Get(pattern)
if result != nil {
if r, ok := result.(string); ok {
return r
}
}
return ""
}
// 返回指定json中的bool
func (p *Json) GetBool(pattern string) bool {
result := p.Get(pattern)
if result != nil {
str := fmt.Sprintf("%v", result)
if str != "" && str != "0" && str != "false" {
return true
}
}
return false
}
// 返回指定json中的float64
func (p *Json) GetFloat64(pattern string) float64 {
result := p.Get(pattern)
if result != nil {
if r, ok := result.(float64); ok {
return r
}
}
return 0
}
// 返回指定json中的float64->int
func (p *Json) GetInt(pattern string) int {
return int(p.GetFloat64(pattern))
}
// 返回指定json中的float64->int64
func (p *Json) GetInt64(pattern string) int64 {
return int64(p.GetFloat64(pattern))
}
// 根据约定字符串方式访问json解析数据参数形如 "items.name.first", "list.0"
// 返回的结果类型的interface{},因此需要自己做类型转换
// 如果找不到对应节点的数据返回nil
func (p *Json) Get(pattern string) interface{} {
var result interface{}
pointer := p.value
array := strings.Split(pattern, ".")
length := len(array)
for i:= 0; i < length; i++ {
switch (*pointer).(type) {
case map[string]interface{}:
if v, ok := (*pointer).(map[string]interface{})[array[i]]; ok {
if i == length - 1 {
result = v
} else {
pointer = &v
}
} else {
return nil
}
case []interface{}:
if isNumeric(array[i]) {
n, err := strconv.Atoi(array[i])
if err == nil && len((*pointer).([]interface{})) > n {
if i == length - 1 {
result = (*pointer).([]interface{})[n]
break;
} else {
pointer = &(*pointer).([]interface{})[n]
}
}
} else {
return nil
}
default:
return nil
}
}
return result
}
// 转换为map[string]interface{}类型,如果转换失败返回nil
func (p *Json) ToMap() map[string]interface{} {
pointer := p.value
switch (*pointer).(type) {
case map[string]interface{}:
return (*pointer).(map[string]interface{})
default:
return nil
}
}
// 转换为[]interface{}类型,如果转换失败返回nil
func (p *Json) ToArray() []interface{} {
pointer := p.value
switch (*pointer).(type) {
case []interface{}:
return (*pointer).([]interface{})
default:
return nil
}
}
// 判断所给字符串是否为数字
func isNumeric(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] < byte('0') || s[i] > byte('9') {
return false
}
}
return true
}

48
g/encoding/gmd5/gmd5.go Normal file
View File

@ -0,0 +1,48 @@
package gmd5
import (
"crypto/md5"
"fmt"
"encoding/json"
"reflect"
"os"
"io"
"g/os/glog"
)
// 将任意类型的变量进行md5摘要(注意map等非排序变量造成的不同结果)
func Encode(v interface{}) string {
h := md5.New()
if "string" == reflect.TypeOf(v).String() {
h.Write([]byte(v.(string)))
} else {
b, err := json.Marshal(v)
if err != nil {
return ""
} else {
h.Write(b)
}
}
return fmt.Sprintf("%x", h.Sum(nil))
}
// 将字符串进行MD5哈希摘要计算
func EncodeString(v string) string {
h := md5.New()
h.Write([]byte(v))
return fmt.Sprintf("%x", h.Sum(nil))
}
// 将文件内容进行MD5哈希摘要计算
func EncodeFile(path string) string {
f, e := os.Open(path)
if e != nil {
glog.Fatalln(e)
}
h := md5.New()
_, e = io.Copy(h, f)
if e != nil {
glog.Fatalln(e)
}
return fmt.Sprintf("%x", h.Sum(nil))
}

36
g/encoding/gsha1/gsha1.go Normal file
View File

@ -0,0 +1,36 @@
package gsha1
import (
"crypto/sha1"
"encoding/hex"
"os"
"io"
"g/os/glog"
"g/encoding/gmd5"
)
// 将任意类型的变量进行SHA摘要(注意map等非排序变量造成的不同结果)
// 内部使用了md5计算因此效率会稍微差一些更多情况请使用 EncodeString
func Encode(v interface{}) string {
return EncodeString(gmd5.Encode(v))
}
// 对字符串行SHA1摘要计算
func EncodeString(s string) string {
r := sha1.Sum([]byte(s))
return hex.EncodeToString(r[:])
}
// 对文件内容进行SHA1摘要计算
func EncodeFile(path string) string {
f, e := os.Open(path)
if e != nil {
glog.Fatalln(e)
}
h := sha1.New()
_, e = io.Copy(h, f)
if e != nil {
glog.Fatalln(e)
}
return hex.EncodeToString(h.Sum(nil))
}

6
g/g.go Normal file
View File

@ -0,0 +1,6 @@
package g
// 框架信息
const VERSION = "0.40"
const AUTHORS = "john<john@johng.cn>"

107
g/net/ghttp/http.go Normal file
View File

@ -0,0 +1,107 @@
package ghttp
import (
"net/http"
"time"
"crypto/tls"
"log"
"net/url"
)
// http客户端
type Client struct {
http.Client
}
// http server结构体
type Server struct {
server http.Server
config ServerConfig
handlerMap HandlerMap
}
// 请求对象
type ClientRequest struct {
http.Request
getvals *url.Values
}
// 客户端请求结果对象
type ClientResponse struct {
http.Response
}
// 服务端请求返回对象
type ServerResponse struct {
http.ResponseWriter
}
// http回调函数
type HandlerFunc func(*ClientRequest, *ServerResponse)
// uri与回调函数的绑定记录表
type HandlerMap map[string]HandlerFunc
// HTTP Server 设置结构体
type ServerConfig struct {
// HTTP Server基础字段
Addr string // 监听IP和端口监听本地所有IP使用":端口"
Handler http.Handler // 默认的处理函数
TLSConfig *tls.Config // TLS配置
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int // 最大的header长度
ErrorLog *log.Logger // 错误日志的处理接口
// gf 扩展信息字段
IndexFiles []string // 默认访问的文件列表
IndexFolder bool // 如果访问目录是否显示目录列表
ServerAgent string // server agent
ServerRoot string // 服务器服务的本地目录根路径
}
// 默认HTTP Server
var defaultServerConfig = ServerConfig {
Addr : ":80",
Handler : nil,
ReadTimeout : 60 * time.Second,
WriteTimeout : 60 * time.Second,
IdleTimeout : 60 * time.Second,
MaxHeaderBytes : 1024,
IndexFiles : []string{"index.html", "index.htm"},
IndexFolder : false,
ServerAgent : "gf",
ServerRoot : "",
}
// 修改默认的http server配置
func SetDefaultServerConfig (c ServerConfig) {
defaultServerConfig = c
}
// 创建一个默认配置的HTTP Server(默认监听端口是80)
func NewServer() (*Server) {
return NewServerByConfig(defaultServerConfig)
}
// 创建一个HTTP Server返回指针
func NewServerByAddr(addr string) (*Server) {
config := defaultServerConfig
config.Addr = addr
return NewServerByConfig(config)
}
// 创建一个HTTP Server
func NewServerByAddrRoot(addr string, root string) (*Server) {
config := defaultServerConfig
config.Addr = addr
config.ServerRoot = root
return NewServerByConfig(config)
}
// 根据输入配置创建一个http server对象
func NewServerByConfig(s ServerConfig) (*Server) {
var server Server
server.SetConfig(s)
return &server
}

122
g/net/ghttp/http_client.go Normal file
View File

@ -0,0 +1,122 @@
package ghttp
import (
"net/http"
"strings"
"time"
)
// http客户端对象指针
func NewClient() (*Client) {
return &Client{}
}
// 设置请求过期时间
func (c *Client) SetTimeOut(t time.Duration) {
c.Timeout = t
}
// GET请求
func (c *Client) Get(url string) *ClientResponse {
return c.Request("GET", url, "")
}
// PUT请求
func (c *Client) Put(url, data string) *ClientResponse {
return c.Request("PUT", url, data)
}
// POST请求提交数据
func (c *Client) Post(url, data string) *ClientResponse {
resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data))
if err != nil {
//glog.Println(err)
return nil
}
r := &ClientResponse{}
r.Response = *resp
return r
}
// DELETE请求
func (c *Client) Delete(url, data string) *ClientResponse {
return c.Request("DELETE", url, data)
}
func (c *Client) Head(url, data string) *ClientResponse {
return c.Request("HEAD", url, data)
}
func (c *Client) Patch(url, data string) *ClientResponse {
return c.Request("PATCH", url, data)
}
func (c *Client) Connect(url, data string) *ClientResponse{
return c.Request("CONNECT", url, data)
}
func (c *Client) Options(url, data string) *ClientResponse{
return c.Request("OPTIONS", url, data)
}
func (c *Client) Trace(url, data string) *ClientResponse {
return c.Request("TRACE", url, data)
}
// 请求并返回response对象
func (c *Client) Request(method, url, data string) *ClientResponse {
req, err := http.NewRequest(strings.ToUpper(method), url, strings.NewReader(data))
if err != nil {
//glog.Println("creating request failed: " + err.Error())
return nil
}
resp, err := c.Do(req)
if err != nil {
//glog.Println("sending request failed: " + err.Error())
return nil
}
r := &ClientResponse{}
r.Response = *resp
return r
}
func Get(url string) *ClientResponse {
return Request("GET", url, "")
}
func Put(url, data string) *ClientResponse {
return Request("PUT", url, data)
}
func Post(url, data string) *ClientResponse {
return Request("PUT", url, data)
}
func Delete(url, data string) *ClientResponse {
return Request("DELETE", url, data)
}
func Head(url, data string) *ClientResponse {
return Request("HEAD", url, data)
}
func Patch(url, data string) *ClientResponse {
return Request("PATCH", url, data)
}
func Connect(url, data string) *ClientResponse{
return Request("CONNECT", url, data)
}
func Options(url, data string) *ClientResponse{
return Request("OPTIONS", url, data)
}
func Trace(url, data string) *ClientResponse {
return Request("TRACE", url, data)
}
func Request(method, url, data string) *ClientResponse {
return NewClient().Request(method, url, data)
}

View File

@ -0,0 +1,29 @@
package ghttp
// 控制器基类
type Controller struct {
Server *Server
}
// 控制器接口
type ControllerApi interface {
Get(r *ClientRequest, w *ServerResponse)
Put(r *ClientRequest, w *ServerResponse)
Post(r *ClientRequest, w *ServerResponse)
Delete(r *ClientRequest, w *ServerResponse)
Head(r *ClientRequest, w *ServerResponse)
Patch(r *ClientRequest, w *ServerResponse)
Connect(r *ClientRequest, w *ServerResponse)
Options(r *ClientRequest, w *ServerResponse)
Trace(r *ClientRequest, w *ServerResponse)
}
func (c *Controller) Get(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Put(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Post(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Delete(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Head(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Patch(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Connect(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Options(r *ClientRequest, w *ServerResponse) {}
func (c *Controller) Trace(r *ClientRequest, w *ServerResponse) {}

155
g/net/ghttp/http_request.go Normal file
View File

@ -0,0 +1,155 @@
package ghttp
import (
"io/ioutil"
"g/encoding/gjson"
)
// 获得get参数
func (r *ClientRequest) GetQuery(k string) []string {
if r.getvals == nil {
values := r.URL.Query()
r.getvals = &values
}
if v, ok := (*r.getvals)[k]; ok {
return v
}
return nil
}
func (r *ClientRequest) GetQueryString(k string) string {
v := r.GetQuery(k)
if v == nil {
return ""
} else {
return v[0]
}
}
func (r *ClientRequest) GetQueryArray(k string) []string {
v := r.GetQuery(k)
if v == nil {
return nil
} else {
return v
}
}
// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值
func (r *ClientRequest) GetQueryMap(defaultMap map[string][]string) map[string][]string {
m := make(map[string][]string)
for k, v := range defaultMap {
v2 := r.GetQueryArray(k)
if v2 == nil {
m[k] = v
} else {
m[k] = v2
}
}
return m
}
// 获得post参数
func (r *ClientRequest) GetPost(k string) []string {
if v, ok := r.PostForm[k]; ok {
return v
}
return nil
}
func (r *ClientRequest) GetPostString(k string) string {
v := r.GetPost(k)
if v == nil {
return ""
} else {
return v[0]
}
}
func (r *ClientRequest) GetPostArray(k string) []string {
v := r.GetPost(k)
if v == nil {
return nil
} else {
return v
}
return nil
}
// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值
func (r *ClientRequest) GetPostMap(defaultMap map[string][]string) map[string][]string {
m := make(map[string][]string)
for k, v := range defaultMap {
if v2, ok := r.PostForm[k]; ok {
m[k] = v2
} else {
m[k] = v
}
}
return m
}
// 获得post或者get提交的参数如果有同名参数那么按照get->post优先级进行覆盖
func (r *ClientRequest) GetRequest(k string) []string {
v := r.GetQuery(k)
if v == nil {
return r.GetPost(k)
}
return v
}
func (r *ClientRequest) GetRequestString(k string) string {
v := r.GetRequest(k)
if v == nil {
return ""
} else {
return v[0]
}
}
func (r *ClientRequest) GetRequestArray(k string) []string {
v := r.GetRequest(k)
if v == nil {
return nil
} else {
return v
}
return nil
}
// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值
func (r *ClientRequest) GetRequestMap(defaultMap map[string][]string) map[string][]string {
m := make(map[string][]string)
for k, v := range defaultMap {
v2 := r.GetRequest(k)
if v2 != nil {
m[k] = v2
} else {
m[k] = v
}
}
return m
}
// 获取原始请求输入字符串
func (r *ClientRequest) GetRaw() string {
result, err := ioutil.ReadAll(r.Body)
if err != nil {
return ""
} else {
return string(result)
}
}
// 获取原始请求输入字符串
func (r *ClientRequest) GetJson() *gjson.Json {
data := r.GetRaw()
if data != "" {
return gjson.DecodeToJson(data)
}
return nil
}

View File

@ -0,0 +1,35 @@
package ghttp
import (
"g/encoding/gjson"
"io/ioutil"
"g/os/glog"
)
type ResponseJson struct {
Result int `json:"result"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// 关闭返回的HTTP链接
func (r *ClientResponse) Close() {
r.Response.Close = true
r.Body.Close()
}
// 返回固定格式的json
func (r *ServerResponse) ResponseJson(result int, message string, data interface{}) {
r.Header().Set("Content-type", "application/json")
r.Write([]byte(gjson.Encode(ResponseJson{ result, message, data })))
}
// 获取返回的数据
func (r *ClientResponse) ReadAll() string {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
glog.Println(err)
return ""
}
return string(body)
}

165
g/net/ghttp/http_server.go Normal file
View File

@ -0,0 +1,165 @@
package ghttp
import (
"net/http"
"strings"
"path/filepath"
"crypto/tls"
"time"
"log"
"regexp"
"g/os/glog"
)
// 执行
func (s *Server)Run() error {
// 底层http server配置
if s.config.Handler == nil {
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
}
s.server = http.Server {
Addr : s.config.Addr,
Handler : s.config.Handler,
ReadTimeout : s.config.ReadTimeout,
WriteTimeout : s.config.WriteTimeout,
IdleTimeout : s.config.IdleTimeout,
MaxHeaderBytes : s.config.MaxHeaderBytes,
}
// 执行端口监听
err := s.server.ListenAndServe()
if err != nil {
glog.Fatalln(err)
}
return err
}
// 获取默认的http server设置
func (h Server)GetDefaultSetting() ServerConfig {
return defaultServerConfig
}
// http server setting设置
// 注意使用该方法进行http server配置时需要配置所有的配置项否则没有配置的属性将会默认变量为空
func (s *Server)SetConfig(c ServerConfig) {
if c.Handler == nil {
c.Handler = http.HandlerFunc(s.defaultHttpHandle)
}
s.config = c
// 需要处理server root最后的目录分隔符号
if s.config.ServerRoot != "" {
s.SetServerRoot(s.config.ServerRoot)
}
// 必需设置默认值的属性
if len(s.config.IndexFiles) < 1 {
s.SetIndexFiles(defaultServerConfig.IndexFiles)
}
if s.config.ServerAgent == "" {
s.SetServerAgent(defaultServerConfig.ServerAgent)
}
}
// 设置http server参数 - Addr
func (s *Server)SetAddr(addr string) {
s.config.Addr = addr
}
// 设置http server参数 - Handler
func (s *Server)SetHandler(handler http.Handler) {
s.config.Handler = handler
}
// 设置http server参数 - TLSConfig
func (s *Server)SetTLSConfig(tls *tls.Config) {
s.config.TLSConfig = tls
}
// 设置http server参数 - ReadTimeout
func (s *Server)SetReadTimeout(t time.Duration) {
s.config.ReadTimeout = t
}
// 设置http server参数 - WriteTimeout
func (s *Server)SetWriteTimeout(t time.Duration) {
s.config.WriteTimeout = t
}
// 设置http server参数 - IdleTimeout
func (s *Server)SetIdleTimeout(t time.Duration) {
s.config.IdleTimeout = t
}
// 设置http server参数 - MaxHeaderBytes
func (s *Server)SetMaxHeaderBytes(b int) {
s.config.MaxHeaderBytes = b
}
// 设置http server参数 - ErrorLog
func (s *Server)SetErrorLog(logger *log.Logger) {
s.config.ErrorLog = logger
}
// 设置http server参数 - IndexFiles
func (s *Server)SetIndexFiles(index []string) {
s.config.IndexFiles = index
}
// 设置http server参数 - IndexFolder
func (s *Server)SetIndexFolder(index bool) {
s.config.IndexFolder = index
}
// 设置http server参数 - ServerAgent
func (s *Server)SetServerAgent(agent string) {
s.config.ServerAgent = agent
}
// 设置http server参数 - ServerRoot
func (s *Server)SetServerRoot(root string) {
s.config.ServerRoot = strings.TrimRight(root, string(filepath.Separator))
}
// 绑定URI到操作函数/方法
// pattern的格式形如/user/list, put:/user, delete:/user
// 支持RESTful的请求格式具体业务逻辑由绑定的处理方法来执行
func (s *Server)BindHandle(pattern string, handler HandlerFunc ) {
if s.handlerMap == nil {
s.handlerMap = make(HandlerMap)
}
key := ""
reg := regexp.MustCompile(`(\w+?)\s*:\s*(.+)`)
result := reg.FindStringSubmatch(pattern)
if len(result) > 1 {
key = strings.ToUpper(result[1]) + ":" + result[2]
} else {
key = strings.TrimSpace(pattern)
}
if _, ok := s.handlerMap[key]; ok {
panic("duplicated http server handler for: " + pattern)
} else {
s.handlerMap[key] = handler
}
}
// 通过映射数组绑定URI到操作函数/方法
func (s *Server)BindHandleByMap(m HandlerMap) {
for p, f := range m {
s.BindHandle(p, f)
}
}
// 绑定控制器控制器需要继承gmvc.ControllerBase对象并实现需要的REST方法
func (s *Server)BindController(uri string, c ControllerApi) {
s.BindHandleByMap(HandlerMap{
"GET:" + uri : c.Get,
"PUT:" + uri : c.Put,
"POST:" + uri : c.Post,
"DELETE:" + uri : c.Delete,
"PATCH:" + uri : c.Patch,
"HEAD:" + uri : c.Head,
"CONNECT:" + uri : c.Connect,
"OPTIONS:" + uri : c.Options,
"TRACE:" + uri : c.Trace,
})
}

View File

@ -0,0 +1,113 @@
package ghttp
import (
"net/http"
"strings"
"path/filepath"
"g/os/gfile"
"os"
"fmt"
"sort"
"net/url"
"g/encoding/ghtml"
)
// 默认HTTP Server处理入口底层默认使用了gorutine调用该接口
func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) {
request := ClientRequest{}
response := ServerResponse {}
request.Request = *r
response.ResponseWriter = w
if f, ok := s.handlerMap[r.URL.Path]; ok {
f(&request, &response)
} else {
method := strings.ToUpper(r.Method)
if f, ok := s.handlerMap[method + ":" + r.URL.Path]; ok {
f(&request, &response)
} else {
s.serveFile(w, r)
}
}
}
// 处理静态文件请求
func (s *Server)serveFile(w http.ResponseWriter, r *http.Request) {
uri := r.URL.String()
if s.config.ServerRoot != "" {
// 获取文件的绝对路径
path := strings.TrimRight(s.config.ServerRoot, string(filepath.Separator))
path = path + uri
path = gfile.RealPath(path)
if (path != "") {
s.doServeFile(w, r, path)
} else {
s.NotFound(w, r)
}
} else {
s.NotFound(w, r)
}
}
// http server静态文件处理
func (s *Server)doServeFile(w http.ResponseWriter, r *http.Request, path string) {
f, err := os.Open(path)
if err != nil {
return
}
info, _ := f.Stat()
if info.IsDir() {
if len(s.config.IndexFiles) > 0 {
for _, file := range s.config.IndexFiles {
fpath := path + "/" + file
if gfile.Exists(fpath) {
f.Close()
s.doServeFile(w, r, fpath)
return
}
}
}
if s.config.IndexFolder {
s.listDir(w, f)
} else {
s.ResponseStatus(w, http.StatusForbidden)
}
} else {
http.ServeContent(w, r, info.Name(), info.ModTime(), f)
}
f.Close()
}
// 目录列表
func (s *Server)listDir(w http.ResponseWriter, f http.File) {
dirs, err := f.Readdir(-1)
if err != nil {
http.Error(w, "Error reading directory", http.StatusInternalServerError)
return
}
sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, "<pre>\n")
for _, d := range dirs {
name := d.Name()
if d.IsDir() {
name += "/"
}
url := url.URL{Path: name}
fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), ghtml.SpecialChars(name))
}
fmt.Fprintf(w, "</pre>\n")
}
// 返回http状态码并使用默认配置的字符串返回信息
func (s *Server)ResponseStatus(w http.ResponseWriter, code int) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
fmt.Fprintln(w, http.StatusText(code))
}
// 404
func (s *Server)NotFound(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
}

153
g/net/gip/ip.go Normal file
View File

@ -0,0 +1,153 @@
package gip
import (
"net"
"strconv"
"strings"
"regexp"
"fmt"
)
// ip字符串转为整形
func Ip2long(ipstr string) (ip uint32) {
r := `^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$`
reg, err := regexp.Compile(r)
if err != nil {
return
}
ips := reg.FindStringSubmatch(ipstr)
if ips == nil {
return
}
ip1, _ := strconv.Atoi(ips[1])
ip2, _ := strconv.Atoi(ips[2])
ip3, _ := strconv.Atoi(ips[3])
ip4, _ := strconv.Atoi(ips[4])
if ip1>255 || ip2>255 || ip3>255 || ip4 > 255 {
return
}
ip += uint32(ip1 * 0x1000000)
ip += uint32(ip2 * 0x10000)
ip += uint32(ip3 * 0x100)
ip += uint32(ip4)
return
}
// ip整形转为字符串
func Long2ip(ip uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
}
// 获得ip的网段例如192.168.2.102 -> 192.168.2
func GetSegment(ip string) string {
r := `^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$`
reg, err := regexp.Compile(r)
if err != nil {
return ""
}
ips := reg.FindStringSubmatch(ip)
if ips == nil {
return ""
}
return fmt.Sprintf("%s.%s.%s", ips[1], ips[2], ips[3])
}
// 解析地址形如192.168.1.1:80 -> 192.168.1.1, 80
func ParseAddress(addr string) (string, int) {
r := `^(.+):(\d+)$`
reg, err := regexp.Compile(r)
if err != nil {
return "", 0
}
result := reg.FindStringSubmatch(addr)
if result != nil {
i, _ := strconv.Atoi(result[2])
return result[1], i
}
return "", 0
}
// 获取本地局域网ip列表
func IntranetIP() (ips []string, err error) {
ips = make([]string, 0)
ifaces, e := net.Interfaces()
if e != nil {
return ips, e
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
if iface.Flags & net.FlagLoopback != 0 {
continue // loopback interface
}
// ignore warden bridge
if strings.HasPrefix(iface.Name, "w-") {
continue
}
addrs, e := iface.Addrs()
if e != nil {
return ips, e
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
continue
}
ip = ip.To4()
if ip == nil {
continue // not an ipv4 address
}
ipStr := ip.String()
if IsIntranet(ipStr) {
ips = append(ips, ipStr)
}
}
}
return ips, nil
}
// 判断所给ip是否为局域网ip
// A类 10.0.0.0--10.255.255.255
// B类 172.16.0.0--172.31.255.255
// C类 192.168.0.0--192.168.255.255
func IsIntranet(ipStr string) bool {
// ip协议保留的局域网ip
if strings.HasPrefix(ipStr, "10.") || strings.HasPrefix(ipStr, "192.168.") {
return true
}
if strings.HasPrefix(ipStr, "172.") {
// 172.16.0.0 - 172.31.255.255
arr := strings.Split(ipStr, ".")
if len(arr) != 4 {
return false
}
second, err := strconv.ParseInt(arr[1], 10, 64)
if err != nil {
return false
}
if second >= 16 && second <= 31 {
return true
}
}
return false
}

87
g/net/gscanner/scanner.go Normal file
View File

@ -0,0 +1,87 @@
// 局域网端口扫描
package gscanner
import (
"net"
"g/net/gip"
"fmt"
"errors"
"sync"
"time"
)
type scanner struct {
timeout time.Duration
}
// 初始化一个扫描器
func New() *scanner {
return &scanner{
6*time.Second,
}
}
// 设置超时时间,注意这个时间是每一次扫描的超时时间,而不是总共的超时时间
func (s *scanner) SetTimeout(t time.Duration) *scanner {
s.timeout = t
return s
}
// 异步TCP扫描网段及端口如果扫描的端口是打开的那么将链接给定给回调函数进行调用
// 注意startIp和endIp需要是同一个网段否则会报错,并且回调函数不会执行
func (s *scanner) ScanIp(startIp string, endIp string, port int, callback func(net.Conn)) error {
if callback == nil {
return errors.New("callback function should not be nil")
}
var waitGroup sync.WaitGroup
startIplong := gip.Ip2long(startIp)
endIplong := gip.Ip2long(endIp)
result := endIplong - startIplong
if startIplong == 0 || endIplong == 0 {
return errors.New("invalid startip or endip: ipv4 string should be given")
}
if result < 0 || result > 255 {
return errors.New("invalid startip and endip: startip and endip should be in the same ip segment")
}
for i := startIplong; i <= endIplong; i++ {
waitGroup.Add(1)
go func(ip string) {
//fmt.Println("scanning:", ip)
// 这里必需设置超时时间
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), s.timeout)
if err == nil {
callback(conn)
conn.Close()
}
//fmt.Println("scanning:", ip, "done")
waitGroup.Done()
}(gip.Long2ip(i))
}
waitGroup.Wait()
return nil
}
// 扫描目标主机打开的端口列表
func (s *scanner) ScanPort(ip string, callback func(net.Conn)) error {
if callback == nil {
return errors.New("callback function should not be nil")
}
var waitGroup sync.WaitGroup
for i := 0; i <= 65536; i++ {
waitGroup.Add(1)
//fmt.Println("scanning:", i)
go func(port int) {
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), s.timeout)
if err == nil {
callback(conn)
conn.Close()
}
waitGroup.Done()
}(i)
}
waitGroup.Wait()
return nil
}

78
g/net/gsmtp/smtp.go Normal file
View File

@ -0,0 +1,78 @@
package gsmtp
import (
"encoding/base64"
"fmt"
"net/smtp"
"strings"
)
// 示例:
// s := smtp.New("smtp.exmail.qq.com:25", "notify@a.com", "password")
// glog.Println(s.SendMail("notify@a.com", "ulric@b.com;rain@c.com", "这是subject", "这是body,<font color=red>red</font>"))
type Smtp struct {
Address string
Username string
Password string
}
func New(address, username, password string) *Smtp {
return &Smtp{
Address: address,
Username: username,
Password: password,
}
}
func (this *Smtp) SendMail(from, tos, subject, body string, contentType ...string) error {
if this.Address == "" {
return fmt.Errorf("address is necessary")
}
hp := strings.Split(this.Address, ":")
if len(hp) != 2 {
return fmt.Errorf("address format error")
}
arr := strings.Split(tos, ";")
count := len(arr)
safeArr := make([]string, 0, count)
for i := 0; i < count; i++ {
if arr[i] == "" {
continue
}
safeArr = append(safeArr, arr[i])
}
if len(safeArr) == 0 {
return fmt.Errorf("tos invalid")
}
tos = strings.Join(safeArr, ";")
b64 := base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
header := make(map[string]string)
header["From"] = from
header["To"] = tos
header["Subject"] = fmt.Sprintf("=?UTF-8?B?%s?=", b64.EncodeToString([]byte(subject)))
header["MIME-Version"] = "1.0"
ct := "text/plain; charset=UTF-8"
if len(contentType) > 0 && contentType[0] == "html" {
ct = "text/html; charset=UTF-8"
}
header["Content-Type"] = ct
header["Content-Transfer-Encoding"] = "base64"
message := ""
for k, v := range header {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + b64.EncodeToString([]byte(body))
auth := smtp.PlainAuth("", this.Username, this.Password, hp[0])
return smtp.SendMail(this.Address, auth, from, strings.Split(tos, ";"), []byte(message))
}

29
g/net/gtcp/tcp.go Normal file
View File

@ -0,0 +1,29 @@
package gtcp
import (
"net"
"g/os/glog"
)
// tcp server结构体
type gTcpServer struct {
address string
listener *net.TCPListener
handler func (net.Conn)
}
// 创建一个tcp server对象
func NewServer (address string, handler func (net.Conn)) *gTcpServer {
tcpaddr, err := net.ResolveTCPAddr("tcp4", address)
if err != nil {
glog.Fatalln(err)
return nil
}
listen, err := net.ListenTCP("tcp", tcpaddr)
if err != nil {
glog.Fatalln(err)
return nil
}
return &gTcpServer{ address, listen, handler}
}

21
g/net/gtcp/tcp_server.go Normal file
View File

@ -0,0 +1,21 @@
package gtcp
import (
"g/os/glog"
)
// 执行监听
func (s *gTcpServer) Run() {
if s == nil || s.listener == nil {
glog.Println("start running failed: socket address bind failed")
return
}
for {
conn, err := s.listener.Accept()
if err != nil {
glog.Error(err)
} else if conn != nil {
go s.handler(conn)
}
}
}

29
g/net/gudp/udp.go Normal file
View File

@ -0,0 +1,29 @@
package gudp
import (
"net"
"log"
)
// tcp server结构体
type gUdpServer struct {
address string
listener *net.UDPConn
handler func (*net.UDPConn)
}
// 创建一个tcp server对象
func NewServer (address string, handler func (*net.UDPConn)) *gUdpServer {
tcpaddr, err := net.ResolveUDPAddr("udp4", address)
if err != nil {
glog.Println(err)
return nil
}
listen, err := net.ListenUDP("udp", tcpaddr)
if err != nil {
glog.Println(err)
return nil
}
return &gUdpServer{ address, listen, handler}
}

19
g/net/gudp/udp_server.go Normal file
View File

@ -0,0 +1,19 @@
package gudp
import "log"
// 执行监听
func (s *gUdpServer) Run() {
if s == nil || s.listener == nil {
glog.Println("start running failed: socket address bind failed")
return
}
if s.handler == nil {
glog.Println("start running failed: socket handler not defined")
return
}
for {
s.handler(s.listener)
}
}

13
g/net/gurl/url.go Normal file
View File

@ -0,0 +1,13 @@
package gurl
import "net/url"
// url encode string, is + not %20
func Encode(str string) string {
return url.QueryEscape(str)
}
// url decode string
func Decode(str string) (string, error) {
return url.QueryUnescape(str)
}

233
g/os/gcache/gcache.go Normal file
View File

@ -0,0 +1,233 @@
package gcache
import (
"sync"
"g/util/gtime"
"time"
"g/encoding/ghash"
)
const (
gCACHE_GROUP_SIZE = 4 // 缓存分区大小不能超过uint8的最大值
)
type Cache struct {
sync.RWMutex
m map[uint8]*CacheMap // 以分区大小数字作为索引
}
type CacheMap struct {
sync.RWMutex
deleted bool // 对象是否已删除以便判断停止goroutine
m map[string]CacheItem // 键值对
}
type CacheItem struct {
v interface{} // 缓存键值
e int64 // 过期时间
}
// 全局缓存管理对象
var cache *Cache = New()
// Cache对象按照缓存键名首字母做了分组
func New() *Cache {
c := &Cache {
m : make(map[uint8]*CacheMap),
}
var i uint8 = 0
for ; i < gCACHE_GROUP_SIZE; i++ {
m := &CacheMap {
m : make(map[string]CacheItem),
}
c.m[i] = m
go m.autoClearLoop()
}
return c
}
// (使用全局KV缓存对象)设置kv缓存键值对过期时间单位为毫秒
func Set(k string, v interface{}, expired int64) {
cache.Set(k, v, expired)
}
// (使用全局KV缓存对象)批量设置kv缓存键值对过期时间单位为毫秒
func BatchSet(m map[string]interface{}, expired int64) {
cache.BatchSet(m, expired)
}
// (使用全局KV缓存对象)获取指定键名的值
func Get(k string) interface{} {
return cache.Get(k)
}
// (使用全局KV缓存对象)删除指定键值对
func Remove(k string) {
cache.Remove(k)
}
// (使用全局KV缓存对象)批量删除指定键值对
func BatchRemove(l []string) {
cache.BatchRemove(l)
}
// 设置kv缓存键值对过期时间单位为毫秒
func (c *Cache) Set(k string, v interface{}, expired int64) {
c.RLock()
c.m[c.getIndex(k)].Set(k, v, expired)
c.RUnlock()
}
// 批量设置
func (c *Cache) BatchSet(m map[string]interface{}, expired int64) {
c.RLock()
for k, v := range m {
c.m[c.getIndex(k)].Set(k, v, expired)
}
c.RUnlock()
}
// 获取指定键名的值
func (c *Cache) Get(k string) interface{} {
c.RLock()
r := c.m[c.getIndex(k)].Get(k)
c.RUnlock()
return r
}
// 删除指定键值对
func (c *Cache) Remove(k string) {
c.RLock()
c.m[c.getIndex(k)].Remove(k)
c.RUnlock()
}
// 批量删除键值对
func (c *Cache) BatchRemove(l []string) {
c.RLock()
for _, k := range l {
c.m[c.getIndex(k)].Remove(k)
}
c.RUnlock()
}
// 获得所有的键名,组成字符串数组返回
func (c *Cache) Keys() []string {
l := make([]string, 0)
c.RLock()
for _, cm := range c.m {
cm.RLock()
for k2, _ := range cm.m {
l = append(l, k2)
}
cm.RUnlock()
}
c.RUnlock()
return l
}
// 获得所有的值,组成数组返回
func (c *Cache) Values() []interface{} {
l := make([]interface{}, 0)
c.RLock()
for _, cm := range c.m {
cm.RLock()
for _, v2 := range cm.m {
l = append(l, v2.v)
}
cm.RUnlock()
}
c.RUnlock()
return l
}
// 获得缓存对象的键值对数量
func (c *Cache) Size() int {
var size int
c.RLock()
for _, cm := range c.m {
cm.RLock()
size += len(cm.m)
cm.RUnlock()
}
c.RUnlock()
return size
}
// 删除缓存对象
func (c *Cache) Close() {
c.RLock()
for _, cm := range c.m {
cm.Lock()
cm.deleted = true
cm.Unlock()
}
c.RUnlock()
c.Lock()
c.m = nil
c.Unlock()
}
// 计算缓存的索引
func (c *Cache) getIndex(k string) uint8 {
return uint8(ghash.BKDRHash([]byte(k)) % gCACHE_GROUP_SIZE)
}
// 设置kv缓存键值对过期时间单位为毫秒
func (cm *CacheMap) Set(k string, v interface{}, expired int64) {
var e int64
if expired > 0 {
e = gtime.Millisecond() + int64(expired)
}
cm.Lock()
cm.m[k] = CacheItem{v: v, e: e}
cm.Unlock()
}
// 获取指定键名的值
func (cm *CacheMap) Get(k string) interface{} {
var v interface{}
cm.RLock()
if r, ok := cm.m[k]; ok {
if r.e > 0 && r.e < gtime.Millisecond() {
v = nil
} else {
v = r.v
}
}
cm.RUnlock()
return v
}
// 删除指定键值对
func (cm *CacheMap) Remove(k string) {
cm.Lock()
delete(cm.m, k)
cm.Unlock()
}
// 自动清理过期键值对(每间隔60秒执行)
func (cm *CacheMap) autoClearLoop() {
for !cm.deleted {
expired := make([]string, 0)
cm.RLock()
for k, v := range cm.m {
if v.e > 0 && v.e < gtime.Millisecond() {
expired = append(expired, k)
}
}
cm.RUnlock()
if len(expired) > 0 {
cm.Lock()
for _, k := range expired {
delete(cm.m, k)
}
cm.Unlock()
}
time.Sleep(60 * time.Second)
}
}

145
g/os/gconsole/gconsole.go Normal file
View File

@ -0,0 +1,145 @@
package gconsole
import (
"os"
"regexp"
"errors"
"strconv"
"strings"
)
// 命令行参数列表
type gConsoleValue struct {
values []string
}
// 命令行选项列表
type gConsoleOption struct {
options map[string]string
}
// 终端管理对象(全局)
var Value gConsoleValue // console终端参数-命令参数列表
var Option gConsoleOption // console终端参数-选项参数列表
var cmdFuncMap = make(map[string]func()) // 终端命令及函数地址对应表
// 检查并初始化console参数在包加载的时候触发
// 初始化时执行,不影响运行时性能
func init() {
Option.options = make(map[string]string)
reg := regexp.MustCompile(`\-\-{0,1}(\w+?)=(.+)`)
for i := 0; i < len(os.Args); i++ {
result := reg.FindStringSubmatch(os.Args[i])
if len(result) > 1 {
Option.options[result[1]] = result[2]
} else {
Value.values = append(Value.values, os.Args[i])
}
}
}
// 返回所有的命令行参数values
func (c gConsoleValue) GetAll() []string {
return c.values
}
// 返回所有的命令行参数options
func (c gConsoleOption) GetAll() map[string]string {
return c.options
}
// 获得一条指定索引位置的value参数
func (c gConsoleValue) Get(index uint8) string {
if index < uint8(len(c.values)) {
return c.values[index]
}
return ""
}
// 类型转换
func (c gConsoleValue) GetInt(key uint8) int {
v := c.Get(key)
if v != "" {
i, _ := strconv.Atoi(v)
return i
}
return 0
}
// 类型转换bool
func (c gConsoleValue) GetBool(key uint8) bool {
v := c.Get(key)
v = strings.ToLower(v)
if v != "" && v != "0" && v != "false" {
return true
}
return false
}
// 获得一条指定索引位置的option参数
func (c gConsoleOption) Get(key string) string {
option, ok := c.options[key]
if ok {
return option
}
return ""
}
// 类型转换int
func (c gConsoleOption) GetInt(key string) int {
v := c.Get(key)
if v != "" {
i, _ := strconv.Atoi(v)
return i
}
return 0
}
// 类型转换bool
func (c gConsoleOption) GetBool(key string) bool {
v := c.Get(key)
v = strings.ToLower(v)
if v != "" && v != "0" && v != "false" {
return true
}
return false
}
// 绑定命令行参数及对应的命令函数,注意参数是函数的内存地址
// 如果操作失败返回错误信息
func BindHandle (cmd string, f func()) error {
_, ok := cmdFuncMap[cmd]
if ok {
return errors.New("duplicated handle for command:" + cmd)
} else {
cmdFuncMap[cmd] = f
return nil
}
}
// 执行命令对应的函数
func RunHandle (cmd string) error {
handle, ok := cmdFuncMap[cmd]
if ok {
handle()
return nil
} else {
return errors.New("no handle found for command:" + cmd)
}
}
// 自动识别命令参数并执行命令参数对应的函数
func AutoRun () error {
cmd := Value.Get(1);
if cmd != "" {
if handle, ok := cmdFuncMap[cmd]; ok {
handle()
return nil
} else {
return errors.New("no handle found for command:" + cmd)
}
} else {
return errors.New("no command found")
}
}

426
g/os/gfile/gfile.go Normal file
View File

@ -0,0 +1,426 @@
package gfile
import (
"os"
"path/filepath"
"io"
"io/ioutil"
"sort"
"fmt"
"time"
"strings"
"bytes"
"os/exec"
"errors"
"os/user"
"runtime"
)
// 封装了常用的文件操作方法如需更详细的文件控制请查看官方os包
// 文件分隔符
var Separator = string(filepath.Separator)
// 给定文件的绝对路径创建文件
func Mkdir(path string) error {
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
return nil
}
// 给定文件的绝对路径创建文件
func Create(path string) error {
f, err := os.Create(path)
if err != nil {
return err
}
f.Close()
return nil
}
// 打开文件
func Open(path string) (*os.File, error) {
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return nil, err
}
return f, nil
}
// 打开文件
func OpenWithFlag(path string, flag int) (*os.File, error) {
f, err := os.OpenFile(path, flag, 0755)
if err != nil {
return nil, err
}
return f, nil
}
// 判断所给路径文件/文件夹是否存在
func Exists(path string) bool {
_, err := os.Stat(path)
if err != nil {
if os.IsExist(err) {
return true
}
return false
}
return true
}
// 判断所给路径是否为文件夹
func IsDir(path string) bool {
s, err := os.Stat(path)
if err != nil {
return false
}
return s.IsDir()
}
// 判断所给路径是否为文件
func IsFile(path string) bool {
return !IsDir(path)
}
// 获取文件或目录信息
func Info(path string) *os.FileInfo {
info, err := os.Stat(path)
if err != nil {
return nil
}
return &info
}
// 修改时间
func MTime(path string) int64 {
f, e := os.Stat(path)
if e != nil {
return 0
}
return f.ModTime().Unix()
}
// 文件大小(bytes)
func Size(path string) int64 {
f, e := os.Stat(path)
if e != nil {
return 0
}
return f.Size()
}
// 格式化文件大小
func ReadableSize(path string) string {
return FormatSize(float64(Size(path)))
}
// 格式化文件大小
func FormatSize(raw float64) string {
var t float64 = 1024
var d float64 = 1
if raw < t {
return fmt.Sprintf("%.2fB", raw/d)
}
d *= 1024
t *= 1024
if raw < t {
return fmt.Sprintf("%.2fK", raw/d)
}
d *= 1024
t *= 1024
if raw < t {
return fmt.Sprintf("%.2fM", raw/d)
}
d *= 1024
t *= 1024
if raw < t {
return fmt.Sprintf("%.2fG", raw/d)
}
d *= 1024
t *= 1024
if raw < t {
return fmt.Sprintf("%.2fT", raw/d)
}
d *= 1024
t *= 1024
if raw < t {
return fmt.Sprintf("%.2fP", raw/d)
}
return "TooLarge"
}
// 文件移动/重命名
func Move(src string, dst string) error {
return os.Rename(src, dst)
}
// 文件移动/重命名
func Rename(src string, dst string) error {
return Move(src, dst)
}
// 文件复制
func Copy(src string, dst string) error {
srcFile, err := os.Open(src)
if err != nil {
return err
}
dstFile, err := os.Create(dst)
if err != nil {
return err
}
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
err = dstFile.Sync()
if err != nil {
return err
}
srcFile.Close()
dstFile.Close()
return nil
}
// 文件/目录删除
func Remove(path string) error {
return os.RemoveAll(path)
}
// 文件是否可
func IsReadable(path string) bool {
result := true
file, err := os.OpenFile(path, os.O_RDONLY, 0666)
if err != nil {
result = false
}
file.Close()
return result
}
// 文件是否可写
func IsWritable(path string) bool {
result := true
if IsDir(path) {
// 如果是目录,那么创建一个临时文件进行写入测试
tfile := strings.TrimRight(path, Separator) + Separator + string(time.Now().UnixNano())
err := Create(tfile)
if err != nil || !Exists(tfile){
result = false
} else {
Remove(tfile)
}
} else {
// 如果是文件,那么判断文件是否可打开
file, err := os.OpenFile(path, os.O_WRONLY, 0666)
if err != nil {
result = false
}
file.Close()
}
return result
}
// 修改文件/目录权限
func Chmod(path string, mode os.FileMode) error {
return os.Chmod(path, mode)
}
// 打开目录,并返回其下一级子目录名称列表,按照文件名称大小写进行排序
func ScanDir(path string) []string {
f, err := os.Open(path)
if err != nil {
return nil
}
list, err := f.Readdirnames(-1)
f.Close()
if err != nil {
return nil
}
sort.Slice(list, func(i, j int) bool { return list[i] < list[j] })
return list
}
// 将所给定的路径转换为绝对路径
// 并判断文件路径是否存在,如果文件不存在,那么返回空字符串
func RealPath(path string) string {
p, err := filepath.Abs(path)
if err != nil {
return ""
}
if !Exists(p) {
return ""
}
return p
}
// (文本)读取文件内容
func GetContents(path string) string {
return string(GetBinContents(path))
}
// (二进制)读取文件内容
func GetBinContents(path string) []byte {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil
}
return data
}
// 写入文件内容
func putContents(path string, data []byte, flag int, perm os.FileMode) error {
// 支持目录递归创建
dir := Dir(path)
if !Exists(dir) {
Mkdir(dir)
}
// 创建/打开文件
f, err := os.OpenFile(path, flag, perm)
if err != nil {
return err
}
defer f.Close()
n, err := f.Write(data)
if err != nil {
return err
} else if n < len(data) {
return io.ErrShortWrite
}
return nil
}
// (文本)写入文件内容
func PutContents(path string, content string) error {
return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
}
// (文本)追加内容到文件末尾
func PutContentsAppend(path string, content string) error {
return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
}
// (二进制)写入文件内容
func PutBinContents(path string, content []byte) error {
return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
}
// (二进制)追加内容到文件末尾
func PutBinContentsAppend(path string, content []byte) error {
return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
}
// 获取当前执行文件的绝对路径
func SelfPath() string {
p, _ := filepath.Abs(os.Args[0])
return p
}
// 获取当前执行文件的目录绝对路径
func SelfDir() string {
return filepath.Dir(SelfPath())
}
// 获取指定文件路径的文件名称
func Basename(path string) string {
return filepath.Base(path)
}
// 获取指定文件路径的目录地址绝对路径
func Dir(path string) string {
return filepath.Dir(path)
}
// 获取指定文件路径的文件扩展名
func Ext(path string) string {
return filepath.Ext(path)
}
// 获取用户主目录
func Home() (string, error) {
u, err := user.Current()
if nil == err {
return u.HomeDir, nil
}
if "windows" == runtime.GOOS {
return homeWindows()
}
return homeUnix()
}
func homeUnix() (string, error) {
if home := os.Getenv("HOME"); home != "" {
return home, nil
}
var stdout bytes.Buffer
cmd := exec.Command("sh", "-c", "eval echo ~$USER")
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
return "", err
}
result := strings.TrimSpace(stdout.String())
if result == "" {
return "", errors.New("blank output when reading home directory")
}
return result, nil
}
func homeWindows() (string, error) {
drive := os.Getenv("HOMEDRIVE")
path := os.Getenv("HOMEPATH")
home := drive + path
if drive == "" || path == "" {
home = os.Getenv("USERPROFILE")
}
if home == "" {
return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
}
return home, nil
}
// 获得文件内容下一个指定字节的位置
func GetNextCharOffset(file *os.File, char string, start int64) int64 {
c := []byte(char)[0]
b := make([]byte, 1)
o := start
for {
_, err := file.ReadAt(b, o)
if err != nil {
return 0
}
if b[0] == c {
return o
}
o++
}
return 0
}
// 获得文件内容中两个offset之间的内容 [start, end)
func GetBinContentByTwoOffsets(file *os.File, start int64, end int64) []byte {
buffer := make([]byte, end - start)
if _, err := file.ReadAt(buffer, start); err != nil {
return nil
}
return buffer
}

115
g/os/gfilepool/gfilepool.go Normal file
View File

@ -0,0 +1,115 @@
package gfilepool
import (
"os"
"g/core/types/glist"
"g/util/gtime"
"time"
"g/core/types/gmap"
"strconv"
)
// 文件指针池
type Pool struct {
path string // 文件绝对路径
flag int // 文件打开标识
list *glist.SafeList // 可用/闲置的文件指针链表
idlemax int // 闲置最大时间,超过该时间则被系统回收(秒)
closed bool // 连接池是否已关闭
}
// 文件指针池指针
type File struct {
pool *Pool // 所属池
file *os.File // 指针对象
expire int64 // 过期时间
}
// 全局指针池expire < 0表示不过期expire = 0表示使用完立即回收expire > 0表示超时回收
var pools *gmap.StringInterfaceMap = gmap.NewStringInterfaceMap()
// 获得文件对象,并自动创建指针池
func OpenWithPool(path string, flag int, expire int) (*File, error) {
key := path + strconv.Itoa(flag) + strconv.Itoa(expire)
result := pools.Get(key)
if result != nil {
return result.(*Pool).File()
}
pool := New(path, flag, expire)
pools.Set(key, pool)
return pool.File()
}
// 创建一个文件指针池expire < 0表示不过期expire = 0表示使用完立即回收expire > 0表示超时回收
func New(path string, flag int, expire int) *Pool {
r := &Pool {
path : path,
flag : flag,
list : glist.NewSafeList(),
idlemax : expire,
}
// 独立的线程执行过期清理工作
if expire != -1 {
go func(p *Pool) {
for !p.closed {
r := p.list.Front()
if r != nil && r.Value != nil {
f := r.Value.(*File)
if f.expire <= gtime.Second() {
if f.file != nil {
f.file.Close()
}
p.list.Remove(r)
continue
}
}
time.Sleep(3 * time.Second)
}
}(r)
}
return r
}
// 获得一个文件打开指针
func (p *Pool) File() (*File, error) {
if p.list.Len() > 0 {
for {
r := p.list.PopBack()
if r != nil {
f := r.(*File)
if f.expire > gtime.Second() {
return f, nil
} else if f.file != nil {
f.file.Close()
f.file = nil
}
} else {
break;
}
}
}
file, err := os.OpenFile(p.path, p.flag, 0755)
if err != nil {
return nil, err
}
return &File {
pool : p,
file : file,
}, nil
}
// 关闭指针池
func (p *Pool) Close() {
p.closed = true
}
// 获得底层文件指针
func (f *File) File() *os.File {
return f.file
}
// 关闭指针链接(软关闭)
func (f *File) Close() {
f.expire = gtime.Second() + int64(f.pool.idlemax)
f.pool.list.PushFront(f)
}

View File

@ -0,0 +1,186 @@
// 文件空间管理, 可用于文件碎片空间维护及再利用,支持自动合并连续碎片空间
package gfilespace
import (
"sync"
"g/core/types/gbtree"
)
// 文件空间管理结构体
type Space struct {
mu sync.RWMutex // 并发操作锁
blocks *gbtree.BTree // 所有的空间块构建的B+树
sizetr *gbtree.BTree // 空间块大小构建的B+树
sizemap map[int]*gbtree.BTree // 按照空间块大小构建的索引哈希表便于检索每个表项是一个B+树
}
// 文件空闲块
type Block struct {
index int // 文件偏移量
size int // 区块大小(byte)
}
// 用于B+树的接口具体实现定义
func (block *Block) Less(item gbtree.Item) bool {
if block.index < item.(*Block).index {
return true
}
return false
}
// 创建一个空间管理器
func New() *Space {
return &Space {
blocks : gbtree.New(10),
sizetr : gbtree.New(5),
sizemap : make(map[int]*gbtree.BTree),
}
}
// 添加空闲空间到管理器
func (space *Space) addBlock(index int, size int) {
block := &Block{index, size}
// 插入进全局树
space.blocks.ReplaceOrInsert(block)
// 插入进入索引表
space.insertIntoSizeMap(block)
// 对插入的数据进行合并检测
space.checkMerge(block)
}
// 获取指定block的前一项block
func (space *Space) getPrevBlock(block *Block) *Block {
var pblock *Block = nil
space.blocks.DescendLessOrEqual(block, func(item gbtree.Item) bool {
if item.(*Block).index != block.index {
pblock = item.(*Block)
return false
}
return true
})
return pblock
}
// 获取指定block的后一项block
func (space *Space) getNextBlock(block *Block) *Block {
var nblock *Block = nil
space.blocks.AscendGreaterOrEqual(block, func(item gbtree.Item) bool {
if item.(*Block).index != block.index {
nblock = item.(*Block)
return false
}
return true
})
return nblock
}
// 获取指定block的前一项block size
func (space *Space) getPrevBlockSize(size int) int {
psize := 0
space.sizetr.DescendLessOrEqual(gbtree.Int(size), func(item gbtree.Item) bool {
if int(item.(gbtree.Int)) != size {
psize = int(item.(gbtree.Int))
return false
}
return true
})
return psize
}
// 获取指定block的后一项block size
func (space *Space) getNextBlockSize(size int) int {
nsize := 0
space.sizetr.AscendGreaterOrEqual(gbtree.Int(size), func(item gbtree.Item) bool {
if int(item.(gbtree.Int)) != size {
nsize = int(item.(gbtree.Int))
return false
}
return true
})
return nsize
}
// 内部按照索引检查合并
func (space *Space) checkMerge(block *Block) {
// 首先检查插入空间块的前一项往后是否可以合并,如果当前合并失败后,才会判断当前插入项和后续的空间块合并
if b := space.checkMergeOfTwoBlock(space.getPrevBlock(block), block); b.index == block.index {
// 其次检查插入空间块的当前项往后是否可以合并
space.checkMergeOfTwoBlock(block, space.getNextBlock(block))
}
}
// 连续检测两个空间块的合并,返回最后一个无法合并的空间块指针
func (space *Space) checkMergeOfTwoBlock(pblock, block *Block) *Block {
if pblock == nil {
return block
}
if block == nil {
return pblock
}
for {
if pblock.index + int(pblock.size) >= block.index {
space.removeBlock(block)
// 判断是否需要更新大小
if pblock.index + int(pblock.size) < block.index + int(block.size) {
space.removeFromSizeMap(pblock)
pblock.size = block.index + block.size - pblock.index
space.insertIntoSizeMap(pblock)
}
block = space.getNextBlock(pblock)
if block == nil {
return pblock
}
} else {
break
}
}
return block
}
// 插入空间块到索引表
func (space *Space) insertIntoSizeMap(block *Block) {
tree, ok := space.sizemap[block.size]
if !ok {
tree = gbtree.New(10)
space.sizemap[block.size] = tree
}
tree.ReplaceOrInsert(block)
// 插入空间块大小记录表
space.sizetr.ReplaceOrInsert(gbtree.Int(block.size))
}
// 删除一项
func (space *Space) removeBlock(block *Block) {
space.blocks.Delete(block)
space.removeFromSizeMap(block)
}
// 从索引表中删除对应的空间块
func (space *Space) removeFromSizeMap(block *Block) {
if tree, ok := space.sizemap[block.size]; ok {
tree.Delete(block)
// 数据数据为空,那么删除该项哈希记录
if tree.Len() == 0 {
delete(space.sizemap, block.size)
space.sizetr.Delete(gbtree.Int(block.size))
}
}
}
// 获得碎片偏移量
func (block *Block) Index() int {
return block.index
}
// 获得碎片大小
func (block *Block) Size() int {
return block.size
}

View File

@ -0,0 +1,177 @@
package gfilespace
import (
"g/core/types/gbtree"
"g/encoding/gbinary"
)
// 添加空闲空间到管理器
func (space *Space) AddBlock(index int, size int) {
if size <= 0 {
return
}
space.mu.Lock()
defer space.mu.Unlock()
space.addBlock(index, size)
}
// 申请空间,返回文件地址及大小,返回成功后则在管理器中删除该空闲块
func (space *Space) GetBlock(size int) (int, int) {
if size <= 0 {
return -1, 0
}
space.mu.Lock()
defer space.mu.Unlock()
for {
if tree, ok := space.sizemap[size]; ok {
if r := tree.Min(); r != nil {
block := r.(*Block)
space.removeBlock(block)
return block.index, block.size
}
}
size = space.getNextBlockSize(size)
if size == 0 {
break
}
}
return -1, 0
}
// 删除指定索引位置的空间块
func (space *Space) RemoveBlock(index int) {
space.mu.Lock()
defer space.mu.Unlock()
space.removeBlock(&Block{index, 0})
}
// 给定的空间块*整块*是否包含在管理器中
func (space *Space) Contains(index int, size int) bool {
block := &Block{index, size}
if r := space.blocks.Get(block); r != nil {
if r.(*Block).size >= size {
return true
}
} else {
pblock := space.getPrevBlock(block)
if pblock != nil && (pblock.index <= index && (pblock.index + pblock.size) >= (index + size)) {
return true
}
}
return false
}
// 获取索引最小的空间块
func (space *Space) GetMinBlock() *Block {
space.mu.RLock()
defer space.mu.RUnlock()
var block *Block
space.blocks.Ascend(func(item gbtree.Item) bool {
block = item.(*Block)
return true
})
return block
}
// 获取索引最大的空间块
func (space *Space) GetMaxBlock() *Block {
space.mu.RLock()
defer space.mu.RUnlock()
var block *Block
space.blocks.Descend(func(item gbtree.Item) bool {
block = item.(*Block)
return true
})
return block
}
// 获得所有的碎片空间按照index升序排序
func (space *Space) GetAllBlocks() []Block {
space.mu.RLock()
defer space.mu.RUnlock()
blocks := make([]Block, 0)
space.blocks.Ascend(func(item gbtree.Item) bool {
blocks = append(blocks, *(item.(*Block)))
return true
})
return blocks
}
// 获得所有的碎片空间大小列表按照size升序排序
func (space *Space) GetAllSizes() []uint {
space.mu.RLock()
defer space.mu.RUnlock()
sizes := make([]uint, 0)
space.sizetr.Ascend(func(item gbtree.Item) bool {
sizes = append(sizes, uint(item.(gbtree.Int)))
return true
})
return sizes
}
// 获取当前空间管理器中最大的空闲块大小
func (space *Space) GetMaxSize() int {
space.mu.RLock()
defer space.mu.RUnlock()
if item := space.sizetr.Max(); item != nil {
return int(item.(gbtree.Int))
}
return 0
}
// 计算总的空闲空间大小
func (space *Space) SumSize() int {
space.mu.RLock()
defer space.mu.RUnlock()
size := 0
space.blocks.Ascend(func(item gbtree.Item) bool {
size += item.(*Block).size
return true
})
return size
}
// 获取空间块的数量
func (space *Space) Len() int {
space.mu.RLock()
defer space.mu.RUnlock()
return space.blocks.Len()
}
// 导出空间块数据
func (space *Space) Export() []byte {
space.mu.RLock()
defer space.mu.RUnlock()
content := make([]byte, 0)
space.blocks.Ascend(func(item gbtree.Item) bool {
block := item.(*Block)
content = append(content, gbinary.EncodeInt64(int64(block.Index()))...)
content = append(content, gbinary.EncodeInt32(int32(block.Size()))...)
return true
})
return content
}
// 导入空间块数据
func (space *Space) Import(content []byte) {
space.mu.Lock()
defer space.mu.Unlock()
for i := 0; i < len(content); i += 12 {
space.addBlock(
int(gbinary.DecodeToInt64(content[i : i + 8])),
int(gbinary.DecodeToInt32(content[i + 8 : i + 12])),
)
}
}

413
g/os/glog/glog.go Normal file
View File

@ -0,0 +1,413 @@
package glog
import (
"sync"
"os"
"io"
"strings"
"reflect"
"path/filepath"
"time"
"fmt"
"g/os/gfile"
)
type Logger struct {
mutex sync.RWMutex
logio io.Writer
debug bool // 是否允许输出DEBUG信息
logpath string // 日志写入的目录路径
lastlogdate string // 上一次写入日志的日期,例如: 2006-01-02
}
// 默认的日志对象
var logger = New()
// 新建自定义的日志操作对象
func New() *Logger {
return &Logger{
debug : true,
}
}
func SetLogPath(path string) {
logger.SetLogPath(path)
}
func SetDebug(debug bool) {
logger.SetDebug(debug)
}
func GetLogPath() string {
return logger.GetLogPath()
}
func Print(v ...interface{}) {
logger.Print(v ...)
}
func Printf(format string, v ...interface{}) {
logger.Printf(format, v ...)
}
func Println(v ...interface{}) {
logger.Println(v ...)
}
func Printfln(format string, v ...interface{}) {
logger.Printfln(format, v ...)
}
func Fatal(v ...interface{}) {
logger.Fatal(v ...)
}
func Fatalf(format string, v ...interface{}) {
logger.Fatalf(format, v ...)
}
func Fatalln(v ...interface{}) {
logger.Fatalln(v ...)
}
func Fatalfln(format string, v ...interface{}) {
logger.Fatalfln(format, v ...)
}
func Panic(v ...interface{}) {
logger.Panic(v ...)
}
func Panicf(format string, v ...interface{}) {
logger.Panicf(format, v ...)
}
func Panicln(v ...interface{}) {
logger.Panicln(v ...)
}
func Panicfln(format string, v ...interface{}) {
logger.Panicfln(format, v ...)
}
func Info(v ...interface{}) {
logger.Info(v...)
}
func Debug(v ...interface{}) {
logger.Debug(v...)
}
func Notice(v ...interface{}) {
logger.Notice(v...)
}
func Warning(v ...interface{}) {
logger.Warning(v...)
}
func Error(v ...interface{}) {
logger.Error(v...)
}
func Critical(v ...interface{}) {
logger.Critical(v...)
}
func Infof(format string, v ...interface{}) {
logger.Infof(format, v...)
}
func Debugf(format string, v ...interface{}) {
logger.Debugf(format, v...)
}
func Noticef(format string, v ...interface{}) {
logger.Noticef(format, v...)
}
func Warningf(format string, v ...interface{}) {
logger.Warningf(format, v...)
}
func Errorf(format string, v ...interface{}) {
logger.Errorf(format, v...)
}
func Criticalf(format string, v ...interface{}) {
logger.Criticalf(format, v...)
}
func Infofln(format string, v ...interface{}) {
logger.Infofln(format, v...)
}
func Debugfln(format string, v ...interface{}) {
logger.Debugfln(format, v...)
}
func Noticefln(format string, v ...interface{}) {
logger.Noticefln(format, v...)
}
func Warningfln(format string, v ...interface{}) {
logger.Warningfln(format, v...)
}
func Errorfln(format string, v ...interface{}) {
logger.Errorfln(format, v...)
}
func Criticalfln(format string, v ...interface{}) {
logger.Criticalfln(format, v...)
}
func (l *Logger) GetLogIO() io.Writer {
l.mutex.RLock()
r := l.logio
l.mutex.RUnlock()
return r
}
func (l *Logger) GetDebug() bool {
l.mutex.RLock()
r := l.debug
l.mutex.RUnlock()
return r
}
func (l *Logger) GetLogPath() string {
l.mutex.RLock()
r := l.logpath
l.mutex.RUnlock()
return r
}
func (l *Logger) GetLastLogDate() string {
l.mutex.RLock()
r := l.lastlogdate
l.mutex.RUnlock()
return r
}
func (l *Logger) SetLogIO(w io.Writer) {
l.mutex.RLock()
l.logio = w
l.mutex.RUnlock()
}
func (l *Logger) SetDebug(debug bool) {
l.mutex.Lock()
l.debug = debug
l.mutex.Unlock()
}
// 设置日志文件的存储目录路径
func (l *Logger) SetLogPath(path string) {
l.mutex.Lock()
l.logpath = strings.TrimRight(path, string(filepath.Separator))
l.mutex.Unlock()
// 重新检查日志io对象
l.checkLogIO()
}
// 检查文件名称是否已经过期
func (l *Logger) checkLogIO() {
date := time.Now().Format("2006-01-02")
if date != l.GetLastLogDate() {
path := l.GetLogPath()
if path != "" {
if !gfile.Exists(path) {
err := gfile.Mkdir(path)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
}
if !gfile.IsWritable(path) {
fmt.Fprintln(os.Stderr, path + " is no writable for current user")
return
}
l.mutex.Lock()
fname := date + ".log"
fpath := l.logpath + string(filepath.Separator) + fname
fio, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0755)
if err == nil && fio != nil {
if l.logio != nil && reflect.TypeOf(l.logio).String() == "*os.File" {
l.logio.(*os.File).Close()
}
l.logio = fio
} else {
fmt.Fprintln(os.Stderr, err)
}
l.mutex.Unlock()
}
}
}
// 核心打印数据方法(标准输出)
func (l *Logger) stdPrint(s string) {
l.checkLogIO()
l.mutex.Lock()
if l.logio == nil {
fmt.Fprint(os.Stdout, l.format(s))
} else {
fmt.Fprint(l.logio, l.format(s))
}
l.mutex.Unlock()
}
// 核心打印数据方法(标准错误)
func (l *Logger) errPrint(s string) {
l.checkLogIO()
l.mutex.Lock()
if l.logio == nil {
fmt.Fprint(os.Stderr, l.format(s))
} else {
fmt.Fprint(l.logio, l.format(s))
}
l.mutex.Unlock()
}
func (l *Logger) format(s string) string {
return time.Now().Format("2006-01-02 15:04:05 ") + s
}
func (l *Logger) Print(v ...interface{}) {
l.stdPrint(fmt.Sprint(v...))
}
func (l *Logger) Printf(format string, v ...interface{}) {
l.stdPrint(fmt.Sprintf(format, v...))
}
func (l *Logger) Println(v ...interface{}) {
l.stdPrint(fmt.Sprintln(v...))
}
func (l *Logger) Printfln(format string, v ...interface{}) {
l.stdPrint(fmt.Sprintf(format + "\n", v...))
}
func (l *Logger) Fatal(v ...interface{}) {
l.errPrint(fmt.Sprint(v...))
os.Exit(1)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
l.errPrint(fmt.Sprintf(format, v...))
os.Exit(1)
}
func (l *Logger) Fatalln(v ...interface{}) {
l.errPrint(fmt.Sprintln(v...))
os.Exit(1)
}
func (l *Logger) Fatalfln(format string, v ...interface{}) {
l.errPrint(fmt.Sprintf(format + "\n", v...))
os.Exit(1)
}
func (l *Logger) Panic(v ...interface{}) {
s := fmt.Sprint(v...)
l.errPrint(s)
panic(s)
}
func (l *Logger) Panicf(format string, v ...interface{}) {
s := fmt.Sprintf(format, v...)
l.errPrint(s)
panic(s)
}
func (l *Logger) Panicln(v ...interface{}) {
s := fmt.Sprintln(v...)
l.errPrint(s)
panic(s)
}
func (l *Logger) Panicfln(format string, v ...interface{}) {
s := fmt.Sprintf(format + "\n", v...)
l.errPrint(s)
panic(s)
}
func (l *Logger) Info(v ...interface{}) {
l.stdPrint("[INFO] " + fmt.Sprintln(v...))
}
func (l *Logger) Debug(v ...interface{}) {
if l.GetDebug() {
l.stdPrint("[DEBU] " + fmt.Sprintln(v...))
}
}
func (l *Logger) Notice(v ...interface{}) {
l.errPrint("[NOTI] " + fmt.Sprintln(v...))
}
func (l *Logger) Warning(v ...interface{}) {
l.errPrint("[WARN] " + fmt.Sprintln(v...))
}
func (l *Logger) Error(v ...interface{}) {
l.errPrint("[ERRO] " + fmt.Sprintln(v...))
}
func (l *Logger) Critical(v ...interface{}) {
l.errPrint("[CRIT] " + fmt.Sprintln(v...))
}
func (l *Logger) Infof(format string, v ...interface{}) {
l.stdPrint("[INFO] " + fmt.Sprintf(format, v...))
}
func (l *Logger) Debugf(format string, v ...interface{}) {
if l.GetDebug() {
l.stdPrint("[DEBU] " + fmt.Sprintf(format, v...))
}
}
func (l *Logger) Noticef(format string, v ...interface{}) {
l.errPrint("[NOTI] " + fmt.Sprintf(format, v...))
}
func (l *Logger) Warningf(format string, v ...interface{}) {
l.errPrint("[WARN] " + fmt.Sprintf(format, v...))
}
func (l *Logger) Errorf(format string, v ...interface{}) {
l.errPrint("[ERRO] " + fmt.Sprintf(format, v...))
}
func (l *Logger) Criticalf(format string, v ...interface{}) {
l.errPrint("[CRIT] " + fmt.Sprintf(format, v...))
}
func (l *Logger) Infofln(format string, v ...interface{}) {
l.stdPrint("[INFO] " + fmt.Sprintf(format, v...) + "\n")
}
func (l *Logger) Debugfln(format string, v ...interface{}) {
if l.GetDebug() {
l.stdPrint("[DEBU] " + fmt.Sprintf(format, v...) + "\n")
}
}
func (l *Logger) Noticefln(format string, v ...interface{}) {
l.errPrint("[NOTI] " + fmt.Sprintf(format, v...) + "\n")
}
func (l *Logger) Warningfln(format string, v ...interface{}) {
l.errPrint("[WARN] " + fmt.Sprintf(format, v...) + "\n")
}
func (l *Logger) Errorfln(format string, v ...interface{}) {
l.errPrint("[ERRO] " + fmt.Sprintf(format, v...) + "\n")
}
func (l *Logger) Criticalfln(format string, v ...interface{}) {
l.errPrint("[CRIT] " + fmt.Sprintf(format, v...) + "\n")
}

View File

@ -0,0 +1,38 @@
// from https://github.com/influxdata/influxdb/tree/master/pkg/mmap
package gmmap
import (
"os"
"syscall"
"golang.org/x/sys/unix"
)
func Map(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
} else if fi.Size() == 0 {
return nil, nil
}
data, err := unix.Mmap(int(f.Fd()), 0, int(fi.Size()), syscall.PROT_READ, syscall.MAP_SHARED)
if err != nil {
return nil, err
}
return data, nil
}
// Unmap closes the memory-map.
func Unmap(data []byte) error {
if data == nil {
return nil
}
return unix.Munmap(data)
}

21
g/os/gmmap/gmmap_test.go Normal file
View File

@ -0,0 +1,21 @@
package gmmap
import (
"bytes"
"io/ioutil"
"testing"
"g/os/gmmap"
)
func TestMap(t *testing.T) {
data, err := gmmap.Map("mmap_test.go")
if err != nil {
t.Fatalf("Open: %v", err)
}
if exp, err := ioutil.ReadFile("mmap_test.go"); err != nil {
t.Fatalf("ioutil.ReadFile: %v", err)
} else if !bytes.Equal(data, exp) {
t.Fatalf("got %q\nwant %q", string(data), string(exp))
}
}

37
g/os/gmmap/gmmap_unix.go Normal file
View File

@ -0,0 +1,37 @@
// from https://github.com/influxdata/influxdb/tree/master/pkg/mmap
package gmmap
import (
"os"
"syscall"
)
// Map memory-maps a file.
func Map(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
} else if fi.Size() == 0 {
return nil, nil
}
data, err := syscall.Mmap(int(f.Fd()), 0, int(fi.Size()), syscall.PROT_READ, syscall.MAP_SHARED)
if err != nil {
return nil, err
}
return data, nil
}
// Unmap closes the memory-map.
func Unmap(data []byte) error {
if data == nil {
return nil
}
return syscall.Munmap(data)
}

View File

@ -0,0 +1,47 @@
// from https://github.com/influxdata/influxdb/tree/master/pkg/mmap
package gmmap
import (
"os"
"syscall"
"unsafe"
)
// Map memory-maps a file.
func Map(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, err
} else if fi.Size() == 0 {
return nil, nil
}
lo, hi := uint32(fi.Size()), uint32(fi.Size()>>32)
fmap, err := syscall.CreateFileMapping(syscall.Handle(f.Fd()), nil, syscall.PAGE_READONLY, hi, lo, nil)
if err != nil {
return nil, err
}
defer syscall.CloseHandle(fmap)
ptr, err := syscall.MapViewOfFile(fmap, syscall.FILE_MAP_READ, 0, 0, uintptr(fi.Size()))
if err != nil {
return nil, err
}
data := (*[1 << 30]byte)(unsafe.Pointer(ptr))[:fi.Size()]
return data, nil
}
// Unmap closes the memory-map.
func Unmap(data []byte) error {
if data == nil {
return nil
}
return syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&data[0])))
}

150
g/util/gpage/page.go Normal file
View File

@ -0,0 +1,150 @@
package gpage
import (
"math"
"net/http"
"net/url"
"strconv"
)
type Paginator struct {
Request *http.Request
PerPageNums int
MaxPages int
nums int64
pageRange []int
pageNums int
page int
}
func (p *Paginator) PageNums() int {
if p.pageNums != 0 {
return p.pageNums
}
pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums))
if p.MaxPages > 0 {
pageNums = math.Min(pageNums, float64(p.MaxPages))
}
p.pageNums = int(pageNums)
return p.pageNums
}
func (p *Paginator) Nums() int64 {
return p.nums
}
func (p *Paginator) SetNums(nums interface{}) {
p.nums = int64(nums)
}
func (p *Paginator) Page() int {
if p.page != 0 {
return p.page
}
if p.Request.Form == nil {
p.Request.ParseForm()
}
p.page, _ = strconv.Atoi(p.Request.Form.Get("p"))
if p.page > p.PageNums() {
p.page = p.PageNums()
}
if p.page <= 0 {
p.page = 1
}
return p.page
}
func (p *Paginator) Pages() []int {
if p.pageRange == nil && p.nums > 0 {
var pages []int
pageNums := p.PageNums()
page := p.Page()
switch {
case page >= pageNums-4 && pageNums > 9:
start := pageNums - 9 + 1
pages = make([]int, 9)
for i, _ := range pages {
pages[i] = start + i
}
case page >= 5 && pageNums > 9:
start := page - 5 + 1
pages = make([]int, int(math.Min(9, float64(page+4+1))))
for i, _ := range pages {
pages[i] = start + i
}
default:
pages = make([]int, int(math.Min(9, float64(pageNums))))
for i, _ := range pages {
pages[i] = i + 1
}
}
p.pageRange = pages
}
return p.pageRange
}
func (p *Paginator) PageLink(page int) string {
link, _ := url.ParseRequestURI(p.Request.RequestURI)
values := link.Query()
if page == 1 {
values.Del("p")
} else {
values.Set("p", strconv.Itoa(page))
}
link.RawQuery = values.Encode()
return link.String()
}
func (p *Paginator) PageLinkPrev() (link string) {
if p.HasPrev() {
link = p.PageLink(p.Page() - 1)
}
return
}
func (p *Paginator) PageLinkNext() (link string) {
if p.HasNext() {
link = p.PageLink(p.Page() + 1)
}
return
}
func (p *Paginator) PageLinkFirst() (link string) {
return p.PageLink(1)
}
func (p *Paginator) PageLinkLast() (link string) {
return p.PageLink(p.PageNums())
}
func (p *Paginator) HasPrev() bool {
return p.Page() > 1
}
func (p *Paginator) HasNext() bool {
return p.Page() < p.PageNums()
}
func (p *Paginator) IsActive(page int) bool {
return p.Page() == page
}
func (p *Paginator) Offset() int {
return (p.Page() - 1) * p.PerPageNums
}
func (p *Paginator) HasPages() bool {
return p.PageNums() > 1
}
func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator {
p := Paginator{}
p.Request = req
if per <= 0 {
per = 10
}
p.PerPageNums = per
p.SetNums(nums)
return &p
}

56
g/util/grand/rand.go Normal file
View File

@ -0,0 +1,56 @@
package grand
import (
"time"
"math/rand"
)
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
var digits = []rune("0123456789")
// 获得一个 min, max 之间的随机数(min <= x <= max)
func Rand (min, max int) int {
//fmt.Printf("min: %d, max: %d\n", min, max)
if min >= max {
return min
}
rand.Seed(time.Now().UnixNano())
n := rand.Intn(max + 1)
if n < min {
return Rand(min, max)
}
return n
}
// 获得指定长度的随机字符串(可能包含数字和字母)
func RandStr(n int) string {
rand.Seed(time.Now().UnixNano())
b := make([]rune, n)
for i := range b {
if rand.Intn(2) == 1 {
b[i] = digits[rand.Intn(10)]
} else {
b[i] = letters[rand.Intn(52)]
}
}
return string(b)
}
// 获得指定长度的随机数字字符串
func RandDigits(n int) string {
rand.Seed(time.Now().UnixNano())
b := make([]rune, n)
for i := range b {
b[i] = digits[rand.Intn(10)]
}
return string(b)
}
// 获得指定长度的随机字母字符串
func RandLetters(n int) string {
rand.Seed(time.Now().UnixNano())
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(52)]
}
return string(b)
}

14
g/util/gregx/regx.go Normal file
View File

@ -0,0 +1,14 @@
package gregx
import (
"regexp"
)
// 正则表达式是否匹配
func IsMatch(val, pattern string) bool {
match, err := regexp.Match(pattern, []byte(val))
if err != nil {
return false
}
return match
}

57
g/util/gtime/time.go Normal file
View File

@ -0,0 +1,57 @@
package gtime
import (
"time"
)
// 类似与js中的SetTimeout一段时间后执行回调函数
func SetTimeout(t time.Duration, callback func()) {
go func() {
time.Sleep(t)
callback()
}()
}
// 类似与js中的SetInterval每隔一段时间后执行回调函数当回调函数返回true那么继续执行否则终止执行该方法是异步的
// 注意:由于采用的是循环而不是递归操作,因此间隔时间将会以上一次回调函数执行完成的时间来计算
func SetInterval(t time.Duration, callback func() bool) {
go func() {
for {
time.Sleep(t)
r := callback()
if !r {
break;
}
}
}()
}
// 获取当前的纳秒数
func Nanosecond() int64 {
return time.Now().UnixNano()
}
// 获取当前的微秒数
func Microsecond() int64 {
return time.Now().UnixNano()/1e3
}
// 获取当前的毫秒数
func Millisecond() int64 {
return time.Now().UnixNano()/1e6
}
// 获取当前的秒数(时间戳)
func Second() int64 {
return time.Now().UnixNano()/1e9
}
// 获得当前的日期(例如2006-01-02)
func Date() string {
return time.Now().Format("2006-01-02")
}
// 获得当前的时间(例如2006-01-02 15:04:05)
func Datetime() string {
return time.Now().Format("2006-01-02 15:04:05")
}

19
g/util/gutil/util.go Normal file
View File

@ -0,0 +1,19 @@
package gutil
// 便利数组查找字符串索引位置,如果不存在则返回-1
func StringSearch (a []string, s string) int {
for i, v := range a {
if s == v {
return i
}
}
return -1
}
// 判断字符串是否在数组中
func StringInArray (a []string, s string) bool {
return StringSearch(a, s) != -1
}

View File

@ -0,0 +1,63 @@
package main
import (
"fmt"
"g/os/gfile"
"g/encoding/gbinary"
)
type Block struct {
index int
size uint
}
func main() {
content := gfile.GetBinContents("/tmp/blocks")
blocks := make([]Block, 0)
for i := 0; i < len(content); i += 12 {
block := Block{
int(gbinary.DecodeToInt64(content[i : i + 8])),
uint(gbinary.DecodeToUint32(content[i + 8 : i + 12])),
}
blocks = append(blocks, block)
}
for i := 0; i < len(blocks); i++ {
if i + 1 == len(blocks) {
break
}
//fmt.Println(blocks[i].index, blocks[i].size)
if blocks[i].index + int(blocks[i].size) >= blocks[i+1].index {
fmt.Println(blocks[i].index, "+", blocks[i].size, ">=", blocks[i+1].index)
break
}
}
//fs := gfilespace.New()
//blocks := make([]gfilespace.Block, 0)
//for i := 0; i < len(content); i += 12 {
// fs.AddBlock(
// int(gbinary.DecodeToInt64(content[i : i + 8])),
// uint(gbinary.DecodeToUint32(content[i + 8 : i + 12])),
// )
//
//}
//for _, v := range fs.GetAllBlocks() {
// blocks = append(blocks, v)
//}
//
//for i := 0; i < len(blocks); i++ {
// if i + 1 == len(blocks) {
// break
// }
// fmt.Println(blocks[i].Index(), blocks[i].Size())
// if blocks[i].Index() + int(blocks[i].Size()) >= blocks[i+1].Index() {
// fmt.Println(blocks[i].Index(), "+", blocks[i].Size(), ">=", blocks[i+1].Index())
// break
// }
//}
//fmt.Println(blocks)
}

382
gexample/db/mysql/mysql.go Normal file
View File

@ -0,0 +1,382 @@
package main
import (
"fmt"
"time"
"strconv"
"g/database/gdb"
)
// 本文件用于gf框架的mysql数据库操作示例不作为单元测试使用
var db gdb.Link
// 初始化配置及创建数据库
func init () {
gdb.AddDefaultConfigNode(gdb.ConfigNode {
Host : "127.0.0.1",
Port : 3306,
User : "root",
Pass : "123456",
Name : "test2",
Type : "mysql",
Role : "master",
Charset : "utf8",
})
db, _ = gdb.Instance()
//gdb.SetConfig(gdb.ConfigNode {
// Host : "127.0.0.1",
// Port : 3306,
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
//})
//db, _ = gdb.Instance()
//gdb.SetConfig(gdb.Config {
// "default" : gdb.ConfigGroup {
// gdb.ConfigNode {
// Host : "127.0.0.1",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.2",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.3",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.4",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// },
//})
//db, _ = gdb.Instance()
}
// 创建测试数据库
func create() {
fmt.Println("create:")
_, err := db.Exec("CREATE DATABASE IF NOT EXISTS test")
if (err != nil) {
fmt.Println(err)
}
s := `
CREATE TABLE IF NOT EXISTS user (
uid INT(10) UNSIGNED AUTO_INCREMENT,
name VARCHAR(45),
PRIMARY KEY (uid)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
`
_, err = db.Exec(s)
if (err != nil) {
fmt.Println(err)
}
s = `
CREATE TABLE IF NOT EXISTS user_detail (
uid INT(10) UNSIGNED AUTO_INCREMENT,
site VARCHAR(255),
PRIMARY KEY (uid)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
`
_, err = db.Exec(s)
if (err != nil) {
fmt.Println(err)
}
fmt.Println()
}
// 数据写入
func insert() {
fmt.Println("insert:")
r, err := db.Insert("user", &gdb.Map {
"name": "john",
})
if (err == nil) {
uid, err2 := r.LastInsertId()
if err2 == nil {
r, err = db.Insert("user_detail", &gdb.Map {
"uid" : uid,
"site" : "http://johng.cn",
})
if err == nil {
fmt.Printf("uid: %d\n", uid)
} else {
fmt.Println(err)
}
} else {
fmt.Println(err2)
}
} else {
fmt.Println(err)
}
fmt.Println()
}
// 基本sql查询
func query() {
fmt.Println("query:")
list, err := db.GetAll("select * from user limit 2")
if err == nil {
fmt.Println(list)
} else {
fmt.Println(err)
}
fmt.Println()
}
// replace into
func replace() {
fmt.Println("replace:")
r, err := db.Save("user", &gdb.Map {
"uid" : 1,
"name" : "john",
})
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据保存
func save() {
fmt.Println("save:")
r, err := db.Save("user", &gdb.Map {
"uid" : 1,
"name" : "john",
})
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 批量写入
func batchInsert() {
fmt.Println("batchInsert:")
err := db.BatchInsert("user", &gdb.List {
{"name": "john_" + strconv.FormatInt(time.Now().UnixNano(), 10)},
{"name": "john_" + strconv.FormatInt(time.Now().UnixNano(), 10)},
{"name": "john_" + strconv.FormatInt(time.Now().UnixNano(), 10)},
{"name": "john_" + strconv.FormatInt(time.Now().UnixNano(), 10)},
}, 10)
if err != nil {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update1() {
fmt.Println("update1:")
r, err := db.Update("user", &gdb.Map {"name": "john1"}, "uid=?", 1)
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update2() {
fmt.Println("update2:")
r, err := db.Update("user", "name='john2'", "uid=1")
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update3() {
fmt.Println("update3:")
r, err := db.Update("user", "name=?", "uid=?", "john2", 1)
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作1
func linkopSelect1() {
fmt.Println("linkopSelect1:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*, ud.site").Condition("u.uid > ?", 1).Limit(0, 2).Select()
if (err == nil) {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作2
func linkopSelect2() {
fmt.Println("linkopSelect2:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*,ud.site").Condition("u.uid=?", 1).One()
if (err == nil) {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作3
func linkopSelect3() {
fmt.Println("linkopSelect3:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("ud.site").Condition("u.uid=?", 1).Value()
if (err == nil) {
fmt.Println(r.(string))
} else {
fmt.Println(err)
}
fmt.Println()
}
// 错误操作
func linkopUpdate1() {
fmt.Println("linkopUpdate1:")
r, err := db.Table("henghe_setting").Update()
if (err == nil) {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 通过Map指针方式传参方式
func linkopUpdate2() {
fmt.Println("linkopUpdate2:")
r, err := db.Table("user").Data(&gdb.Map{"name" : "john2"}).Condition("name=?", "john").Update()
if (err == nil) {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 通过字符串方式传参
func linkopUpdate3() {
fmt.Println("linkopUpdate3:")
r, err := db.Table("user").Data("name='john3'").Condition("name=?", "john2").Update()
if (err == nil) {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 主从io复用测试在mysql中使用 show full processlist 查看链接信息
func keepPing() {
fmt.Println("keepPing:")
for {
fmt.Println("ping...")
err := db.PingMaster()
if err != nil {
fmt.Println(err)
return
}
err = db.PingSlave()
if err != nil {
fmt.Println(err)
return
}
time.Sleep(1*time.Second)
}
}
// 数据库单例测试在mysql中使用 show full processlist 查看链接信息
func instance() {
fmt.Println("instance:")
db1, _ := gdb.Instance()
db2, _ := gdb.Instance()
db3, _ := gdb.Instance()
for {
fmt.Println("ping...")
db1.PingMaster()
db1.PingSlave()
db2.PingMaster()
db2.PingSlave()
db3.PingMaster()
db3.PingSlave()
time.Sleep(1*time.Second)
}
}
func main() {
//create()
//create()
//insert()
//query()
//replace()
//save()
//batchInsert()
//update1()
//update2()
//update3()
//linkopSelect1()
//linkopSelect2()
//linkopSelect3()
//linkopUpdate1()
//linkopUpdate2()
//linkopUpdate3()
keepPing()
}

284
gexample/db/pgsql/pgsql.go Normal file
View File

@ -0,0 +1,284 @@
package main
import (
"fmt"
"time"
"strconv"
"g/database/gdb"
)
// 本文件用于gf框架的postgresql数据库操作示例不作为单元测试使用
var db gdb.Link
func init () {
gdb.AddDefaultConfigNode(gdb.ConfigNode {
Host : "127.0.0.1",
Port : 5432,
User : "postgres",
Pass : "123456",
Name : "test",
Type : "pgsql",
})
db, _ = gdb.Instance()
}
// 创建测试数据库
func create() {
fmt.Println("create:")
_, err := db.Exec("CREATE SCHEMA IF NOT EXISTS \"test\"")
if (err != nil) {
fmt.Println(err)
}
s := `CREATE TABLE IF NOT EXISTS "user" (
uid int PRIMARY KEY,
name TEXT NOT NULL
)
`
_, err = db.Exec(s)
if (err != nil) {
fmt.Println(err)
}
s = `
CREATE TABLE IF NOT EXISTS user_detail (
uid int PRIMARY KEY,
site TEXT NOT NULL
)
`
_, err = db.Exec(s)
if (err != nil) {
fmt.Println(err)
}
fmt.Println()
}
// 数据写入
func insert() {
fmt.Println("insert:")
r, err := db.Insert("user", &gdb.Map {
"uid" : 1,
"name": "john",
})
if (err == nil) {
uid, err2 := r.LastInsertId()
if err2 == nil {
r, err = db.Insert("user_detail", &gdb.Map {
"uid" : string(uid),
"site" : "http://johng.cn",
})
if err == nil {
fmt.Printf("uid: %d\n", uid)
} else {
fmt.Println(err)
}
} else {
fmt.Println(err2)
}
} else {
fmt.Println(err)
}
fmt.Println()
}
// 基本sql查询
func query() {
fmt.Println("query:")
list, err := db.GetAll("select * from \"user\"")
if err == nil {
fmt.Println(list)
} else {
fmt.Println(err)
}
fmt.Println()
}
// replace into
func replace() {
fmt.Println("replace:")
r, err := db.Save("user", &gdb.Map {
"uid": "1",
"name": "john",
})
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据保存
func save() {
fmt.Println("save:")
r, err := db.Save("user", &gdb.Map {
"uid" : "1",
"name" : "john",
})
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 批量写入
func batchInsert() {
fmt.Println("batchInsert:")
err := db.BatchInsert("user", &gdb.List {
{"name": "john_" + strconv.FormatInt(time.Now().UnixNano(), 10)},
{"name": "john_" + strconv.FormatInt(time.Now().UnixNano(), 10)},
{"name": "john_" + strconv.FormatInt(time.Now().UnixNano(), 10)},
{"name": "john_" + strconv.FormatInt(time.Now().UnixNano(), 10)},
}, 10)
if err != nil {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update1() {
fmt.Println("update1:")
r, err := db.Update("user", &gdb.Map {"name": "john1"}, "uid=?", 1)
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update2() {
fmt.Println("update2:")
r, err := db.Update("user", "name='john2'", "uid=1")
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update3() {
fmt.Println("update3:")
r, err := db.Update("user", "name=?", "uid=?", "john2", 1)
if (err == nil) {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作
func linkopSelect() {
fmt.Println("linkopSelect:")
r, err := db.Table("user u").
LeftJoin("user_detail ud", "u.uid=ud.uid").
Fields("u.*, ud.site").
Condition("u.uid > ?", 1).
Limit(0, 2).Select()
if (err == nil) {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 错误操作
func linkopUpdate1() {
fmt.Println("linkopUpdate1:")
r, err := db.Table("henghe_setting").Update()
if (err == nil) {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 通过Map指针方式传参方式
func linkopUpdate2() {
fmt.Println("linkopUpdate2:")
r, err := db.Table("user").Data(&gdb.Map{"name" : "john2"}).Condition("name=?", "john").Update()
if (err == nil) {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 通过字符串方式传参
func linkopUpdate3() {
fmt.Println("linkopUpdate3:")
r, err := db.Table("user").Data("name='john3'").Condition("name=?", "john2").Update()
if (err == nil) {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 主从io复用测试在mysql中使用 show full processlist 查看链接信息
func keepPing() {
fmt.Println("keepPing:")
for {
fmt.Println("ping...")
db.PingMaster()
db.PingSlave()
time.Sleep(1*time.Second)
}
}
// 数据库单例测试在mysql中使用 show full processlist 查看链接信息
func instance() {
fmt.Println("instance:")
db1, _ := gdb.Instance()
db2, _ := gdb.Instance()
db3, _ := gdb.Instance()
for {
fmt.Println("ping...")
db1.PingMaster()
db1.PingSlave()
db2.PingMaster()
db2.PingSlave()
db3.PingMaster()
db3.PingSlave()
time.Sleep(1*time.Second)
}
}
func main() {
create()
create()
insert()
query()
replace()
save()
batchInsert()
update1()
update2()
update3()
linkopSelect()
linkopUpdate1()
linkopUpdate2()
linkopUpdate3()
}

View File

@ -0,0 +1,20 @@
package main
import (
"strconv"
"fmt"
"g/encoding/ghash"
)
func main () {
m := make(map[uint64]bool)
for i := 0; i < 100000000; i++ {
hash := ghash.BKDRHash64([]byte("key_" + strconv.Itoa(i)))
if _, ok := m[hash]; ok {
fmt.Printf("duplicated hash %d\n", hash)
} else {
m[hash] = true
}
}
}

112
gexample/encoding/json.go Normal file
View File

@ -0,0 +1,112 @@
package main
import (
"fmt"
//"encoding/json"
"g/encoding/gjson"
)
type City struct {
Age string
CityId int
CityName string
ProvinceId int
//CityOrder int
}
func main() {
//data := `[{"CityId":1, "CityName":"北京", "ProvinceId":1, "CityOrder":1}, {"CityId":5, "CityName":"成都", "ProvinceId":27, "CityOrder":1}]`
data := `{"name":"中国","age":31,"list":[["a","b","c"],["d","e","f"]],"items":{"title":"make\"he moon","name":"make'he moon","content":"'[}]{[}he moon"}}`
//data := `[{"CityId":18,"CityName":"西安","ProvinceId":27,"CityOrder":1},{"CityId":53,"CityName":"广州","ProvinceId":27,"CityOrder":1}]`
//data := `{"name" : "中国", "age" : 31, "items":[1,2,3]}`
//data := `[["a","b","c"],["d","e","f"]]`
//data := `["a","b","c"]`
//json := `
//[1,{"a":2},
//{"a":{}},
//{"a":[]},
//{"a":[{}]},
//{"{[a" : "\"2,:3," a ":33}]"}]` // 错误的json
//data := `["a","b","c"` // 错误的json
//data := `,{ "name" : "中国", "age" : 31, "items":[1,2]:}` //错误的json
v := gjson.DecodeToJson(&data)
fmt.Println(v.GetNumber("list"))
//v := map[string]interface{} {
//
// "name" : "中国",
// "age" : 11,
// "list" : []interface{} {
// 1,2,3,4,
// },
//}
//r, _ := json.MarshalIndent(v, "", "\t")
//fmt.Println(string(r))
//s, _ := gjson.Encode(v)
//fmt.Println(*s)
//p, err := gjson.Decode(&data)
//if err == nil {
// //p.Print()
// //fmt.Println(p.Get("0"))
// fmt.Println(p.GetMap("0"))
//} else {
// fmt.Println(err)
//}
//fmt.Println()
//fmt.Println()
////v := make(map[string]interface{})
////i := 31
////j := "john"
////v["age"] = i
////v["name"] = make(map[string]interface{})
////t := v["name"]
////t.(map[string]interface{})["n"] = j
////
////fmt.Println(v)
//var s struct{
// v interface{}
// p interface{}
//}
//v := make(map[string]interface{})
//s.v = v
//s.p = &v
//c := (*s.p.(*map[string]interface{}))
//c["name1"] = "john1"
//
//t := make(map[string]interface{})
//c["/"] = t
//s.p = &t
//t["name2"] = "john2"
//
//c2 := (*s.p.(*map[string]interface{}))
//c2["name3"] = "john3"
//
////t2[2] = 100
//fmt.Println(s.v)
//a := map[string]interface{} {
// "name" : "john",
// "list" : []interface{}{
// 1,2,3, "fuck",
// },
// "item" : map[string]string {
// "n1" : "v1",
// "n2" : "v2",
// "n3" : "v3",
// },
//}
//fmt.Println(json.M)
//
//var a = []int{1,2,3}
//var b = []int{4,5,6, 7,8}
//cc := make([]int, len(a) + 12)
//a = cc
//copy(a, b)
//fmt.Println(a)
//fmt.Println(b)
}

View File

@ -0,0 +1,31 @@
package main
import (
"testing"
"g/encoding/gjson"
"encoding/json"
"log"
)
// go test json_test.go -bench=".*"
var data = `[{"CityId":1, "CityName":"北京", "ProvinceId":1, "CityOrder":1}, {"CityId":5, "CityName":"成都", "ProvinceId":27, "CityOrder":1}]`
func BenchmarkJsonDecode(b *testing.B) {
b.N = 1000000
for i := 0; i < b.N; i++ {
gjson.Decode(data)
}
}
func BenchmarkJsonDecodeByUnmarshal(b *testing.B) {
b.N = 1000000
for i := 0; i < b.N; i++ {
var citys interface{}
if err := json.Unmarshal([]byte(data), &citys); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
//fmt.Println(citys)
}
}

View File

@ -0,0 +1,156 @@
package main
import (
"fmt"
"g/net/ghttp"
)
var kvUrl string = "http://192.168.2.102:4168/kv"
var nodeUrl string = "http://192.168.2.102:4168/node"
var serviceUrl string = "http://192.168.2.102:4168/service"
// kv操作
func addKV() {
c := ghttp.NewClient()
r := c.Put(kvUrl, "{\"name1\":\"john1\", \"name2\":\"john2\"}")
fmt.Println("addKV:", r.ReadAll())
}
func getAllKV() {
c := ghttp.NewClient()
r := c.Get(kvUrl)
fmt.Println("getAllKV:", r.ReadAll())
}
func getOneKV() {
c := ghttp.NewClient()
r := c.Get(kvUrl + "?k=name1")
fmt.Println("getOneKV:", r.ReadAll())
}
func editKV() {
c := ghttp.NewClient()
r := c.Post(kvUrl, "{\"name1\":\"john3\", \"name2\":\"john4\"}")
fmt.Println("editKV:", r.ReadAll())
}
func removeKV() {
c := ghttp.NewClient()
r := c.Delete(kvUrl, "[\"name1\"]")
fmt.Println("removeKV:", r.ReadAll())
}
// node操作
func addNode() {
c := ghttp.NewClient()
r := c.Put(nodeUrl, "[\"172.17.42.1\"]")
fmt.Println("addNode:", r.ReadAll())
}
func getAllNode() {
c := ghttp.NewClient()
r := c.Get(nodeUrl)
fmt.Println("getAllNode:", r.ReadAll())
}
func removeNode() {
c := ghttp.NewClient()
r := c.Delete(nodeUrl, "[\"172.17.42.1\"]")
fmt.Println("removeNode:", r.ReadAll())
}
// service操作
func getAllService() {
c := ghttp.NewClient()
r := c.Get(serviceUrl)
fmt.Println("getAllService:", r.ReadAll())
}
func getOneService() {
c := ghttp.NewClient()
r := c.Get(serviceUrl + "?name=Site Database")
fmt.Println("getOneService:", r.ReadAll())
}
func addDatabaseService() {
c := ghttp.NewClient()
s := `
{
"name" : "Site Database",
"type" : "mysql",
"list" : [
{"host":"192.168.2.102", "port":"3306", "user":"root", "pass":"123456", "database":"test"},
{"host":"192.168.2.124", "port":"3306", "user":"root", "pass":"123456", "database":"tongwujie"}
]
}
`
r := c.Put(serviceUrl, s)
fmt.Println("addDatabaseService:", r.ReadAll())
}
func editDatabaseService() {
c := ghttp.NewClient()
s := `
{
"name" : "Site Database2",
"type" : "mysql",
"list" : [
{"host":"192.168.2.102", "port":"3306", "user":"root", "pass":"123456", "database":"test"},
{"host":"192.168.2.124", "port":"3306", "user":"root", "pass":"123456", "database":"tongwujie"}
]
}
`
r := c.Post(serviceUrl, s)
fmt.Println("editDatabaseService:", r.ReadAll())
}
func removeDatabaseService() {
c := ghttp.NewClient()
r := c.Delete(serviceUrl, "[\"Site Database2\"]")
fmt.Println("removeDatabaseService:", r.ReadAll())
}
func addWebService() {
c := ghttp.NewClient()
s := `
{
"name" : "Site",
"type" : "web",
"list" : [
{"url":"http://baidu.com", "check":"http://itsadeadlink.com"},
{"url":"http://baidu.com"}
]
}
`
r := c.Put(serviceUrl, s)
fmt.Println("addWebService:", r.ReadAll())
}
func editWebService() {
c := ghttp.NewClient()
s := `
{
"name" : "Site2",
"type" : "web",
"list" : [
{"url":"http://baidu.com"},
{"url":"http://baidu.com"}
]
}
`
r := c.Post(serviceUrl, s)
fmt.Println("editWebService:", r.ReadAll())
}
func removeWebService() {
c := ghttp.NewClient()
r := c.Delete(serviceUrl, "[\"Site2\"]")
fmt.Println("removeWebService:", r.ReadAll())
}
func main() {
addWebService()
}

View File

@ -0,0 +1,14 @@
package main
import (
"fmt"
"g/net/ghttp"
)
func main() {
c := ghttp.NewClient()
r := c.Get("http://192.168.2.124")
fmt.Println(r.StatusCode)
}

View File

@ -0,0 +1,29 @@
package main
import (
"net/http"
"io"
"g/net/ghttp"
)
func HelloServer1(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "hello1!\n")
}
func HelloServer2(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "hello2\n")
}
func main() {
s := ghttp.New()
s.SetAddr(":8199")
s.SetIndexFolder(true)
s.SetServerRoot("/home/john/Workspace/")
s.BindHandleByMap(ghttp.HandlerMap {
"/h": HelloServer1,
"/h1": HelloServer1,
"/h2": HelloServer1,
"/h3": HelloServer1,
})
s.BindHandle("/hello1", HelloServer1)
s.BindHandle("/hello2", HelloServer2)
s.Run()
}

25
gexample/net/raft.go Normal file
View File

@ -0,0 +1,25 @@
package main
import (
"g/net/graft"
"g/net/gip"
"log"
)
func main() {
ips, err := gip.IntranetIP()
if err != nil {
log.Println(err)
return
}
for _, ip := range ips {
//fmt.Println(ip)
graft.NewServerByIp(ip).Run()
}
select {
}
}

View File

@ -0,0 +1,29 @@
package main
import (
"net"
"log"
"g/net/graft"
"fmt"
"g/encoding/gjson"
)
func rpcLogSet() {
conn, err := net.Dial("tcp", "192.168.2.124:4167")
if err != nil {
log.Println(err)
return
}
entry := graft.LogRequest{}
entry.Key = "name3"
entry.Value = "john3"
fmt.Println(*gjson.Encode(entry))
e := graft.SendMsg(conn, 100, *gjson.Encode(entry))
fmt.Println(e)
conn.Close()
}
func main() {
rpcLogSet()
}

23
gexample/net/scanner.go Normal file
View File

@ -0,0 +1,23 @@
package main
import (
"g/net/gscanner"
"net"
"fmt"
"time"
)
func main() {
//gscanner.New().SetTimeout(3*time.Second).ScanIp("192.168.2.1", "192.168.2.255", 80, func(conn net.Conn){
// fmt.Println(conn.RemoteAddr())
//})
gscanner.New().SetTimeout(3*time.Second).ScanIp("120.76.249.1", "120.76.249.255", 80, func(conn net.Conn){
fmt.Println(conn.RemoteAddr())
})
//gscanner.New().SetTimeout(6*time.Second).ScanPort("120.76.249.2", func(conn net.Conn){
// //fmt.Println("yes")
// fmt.Println(conn.RemoteAddr())
//})
}

View File

@ -0,0 +1,47 @@
package main
import (
"net"
"fmt"
"g/net/gtcp"
"io"
"log"
"time"
"g/util/gutil"
)
func main() {
gtcp.NewServer(":8999", func(conn net.Conn) {
try := 0
buffersize := 5
data := make([]byte, 0)
for {
buffer := make([]byte, buffersize)
length, err := conn.Read(buffer)
if err != nil {
log.Println(err)
if err != io.EOF {
log.Println("node recieve:", err, "try:", try)
}
if try > 2 {
break;
}
try ++
time.Sleep(100 * time.Millisecond)
} else {
if length == buffersize {
data = gutil.MergeSlice(data, buffer)
} else {
data = gutil.MergeSlice(data, buffer[0:length])
break;
}
}
}
fmt.Println(string(data))
}).Run()
select {
}
}

21
gexample/net/udp.go Normal file
View File

@ -0,0 +1,21 @@
package main
import (
"net"
"fmt"
"os"
)
func main() {
conn, err := net.Dial("udp", "127.0.0.1:8999")
defer conn.Close()
if err != nil {
os.Exit(1)
}
conn.Write([]byte(""))
var msg [20]byte
n, err := conn.Read(msg[0:])
fmt.Println(string(msg[0:n]))
}

View File

@ -0,0 +1,19 @@
package main
import (
"net"
"fmt"
"g/net/gudp"
)
func main() {
gudp.NewServer(":8999", func(conn *net.UDPConn) {
var buf [1024]byte
count, raddr, err := conn.ReadFromUDP(buf[0:])
if err != nil {
return
}
fmt.Println(raddr.String() + ":", string(buf[0:count]))
_, err = conn.WriteToUDP([]byte("hi"), raddr)
}).Run()
}

21
gexample/os/console.go Normal file
View File

@ -0,0 +1,21 @@
package main
import (
"fmt"
"g/os/gconsole"
)
func doEcho() {
fmt.Println("do echo")
}
func main() {
fmt.Println(gconsole.Value.GetAll())
fmt.Println(gconsole.Value.GetIndex(1))
gconsole.BindHandle("echo", doEcho)
gconsole.RunHandle("echo")
gconsole.AutoRun()
}

47
gexample/os/file.go Normal file
View File

@ -0,0 +1,47 @@
package main
import (
"g/os/gfile"
"fmt"
)
var dirpath1 = "/home/john/Workspace/temp/"
var dirpath2 = "/home/john/Workspace/temp/1"
var filepath1 = "/home/john/Workspace/temp/test.php"
var filepath2 = "/tmp/tmp.test"
type BinData struct{
name string
age int
}
func info () {
fmt.Println(gfile.Info(dirpath1))
}
func scanDir() {
files := gfile.ScanDir(dirpath1)
fmt.Println(files)
}
func getContents() {
fmt.Printf("%s\n", gfile.GetContents(filepath1))
}
func putContents() {
fmt.Println(gfile.PutContentsAppend(filepath2, []byte("123")))
}
func putBinContents() {
data := []byte(BinData{"john", 31})
fmt.Println(gfile.PutContents(filepath2, data))
}
func main() {
//info()
//getContents()
//putContents()
putBinContents()
//scanDir()
}

View File

@ -0,0 +1,43 @@
package main
import (
"testing"
"g/os/gcache"
)
var cache *gcache.Cache = gcache.New()
func BenchmarkSet(b *testing.B) {
b.N = 1000000
for i := 0; i < 1000000; i ++ {
cache.Set(string(i), i, 0)
}
}
func BenchmarkSetWithExpire(b *testing.B) {
b.N = 1000000
for i := 0; i < 1000000; i ++ {
cache.Set(string(i), i, 60)
}
}
func BenchmarkGet1(b *testing.B) {
b.N = 1000000
for i := 0; i < 1000000; i ++ {
cache.Get(string(i))
}
}
func BenchmarkGet2(b *testing.B) {
b.N = 1000000
for i := 0; i < 1000000; i ++ {
cache.Get(string(i))
}
}
func BenchmarkRemove(b *testing.B) {
b.N = 1000000
for i := 0; i < 1000000; i ++ {
cache.Remove(string(i))
}
}

70
gexample/os/gfilespace.go Normal file
View File

@ -0,0 +1,70 @@
package main
import (
"g/os/gfilespace"
"fmt"
)
func main() {
//t1 := gtime.Microsecond()
space := gfilespace.New()
space.AddBlock(0, 10)
space.AddBlock(11, 10)
space.AddBlock(23, 10)
fmt.Println(space.GetAllBlocks())
fmt.Println(space.Contains(24, 10))
//t1 := gtime.Microsecond()
//for i := 1; i <= 10; i++ {
// space.AddBlock(i*grand.Rand(0, 10000000), uint(i*10))
// //space.AddBlock(i, uint(i*100))
// //fmt.Println(space.GetAllBlocks())
//}
//fmt.Println("create", gtime.Microsecond() - t1)
//e := space.Export()
//space2 := gfilespace.New()
//fmt.Println(e)
//space2.Import(e)
//fmt.Println(space2.Export())
//t2 := gtime.Microsecond()
//fmt.Println(space.GetBlock(10))
//fmt.Println(space.GetBlock(10))
//fmt.Println(space.GetBlock(10))
//fmt.Println(space.GetBlock(10))
//fmt.Println(space.GetBlock(10))
//fmt.Println(space.GetBlock(10))
//fmt.Println(space.GetBlock(10))
//fmt.Println("get", gtime.Microsecond() - t2)
//
//fmt.Println(space.GetAllBlocks())
//fmt.Println(space.GetAllSizes())
//add block: 1792 192
//[{0 192} {512 192} {768 384} {1408 960}]
//add block: 320 192
//[{0 192} {320 192} {512 192} {768 384} {1408 960}]
//add mt block 1618432 64
//[{1618432 64}]
//[{1618432 64}]
//add mt block 1618496 64
//[{1618432 128}]
//[{1618432 64}]
//space.AddBlock(467264, 64)
//space.AddBlock(467200, 128)
//space.Empty()
//fmt.Println(gtime.Microsecond() - t1)
//fmt.Println(space.GetBlock(15))
//fmt.Println(space.GetBlock(15))
}

60
gexample/other/reflect.go Normal file
View File

@ -0,0 +1,60 @@
package main
import (
"fmt"
//"reflect"
"reflect"
)
//import "reflect"
type gtInterface interface {
Run()
}
type st struct {
age int
name string
}
type mySt struct {
st
}
func (_ st) Echo(str string) {
fmt.Printf("echo(%s)\n", str)
}
func (_ *st) Echo2(str string) {
fmt.Printf("echo2(%s)\n", str)
}
func (_ st) Echo3() {
fmt.Println("echo3()")
}
func Echo3() {
fmt.Println("echo3()")
}
type DefaultFunc func()
func Call(i DefaultFunc) {
i()
//reflect.ValueOf(i).Call([]reflect.Value{})
}
func main() {
s := st {16,"john"}
//p := reflect.ValueOf("halloo")
v := reflect.ValueOf(s)
//v2 := reflect.ValueOf(&s)
//// 调用st结构体的方法
//v.MethodByName("Echo").Call([]reflect.Value{p})
//// 我们需要调用的是实体结构体指针的方法注意v2与v2的区别以及方法定义的区别
//v2.MethodByName("Echo2").Call([]reflect.Value{p})
//v.MethodByName()
fmt.Println(v.Type())
}

261
gexample/other/test.go Normal file
View File

@ -0,0 +1,261 @@
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.RWMutex
mu.Lock()
mu.RLock()
fmt.Println(1)
mu.RUnlock()
mu.Unlock()
return
//a := 1497965
//for i := 0; i < 10000; i++ {
// fmt.Println(a*i)
//}
//
//fmt.Println(math.MaxUint64)
//m := make(map[int]int, 0)
//for i := 0; i < 10000000; i ++ {
// m[i] = i
//}
//t1 := gtime.Microsecond()
//for i := 0; i < 10; i ++ {
// if _, ok := m[i]; ok {
//
// }
//}
//b := make([]byte, 100000)
//removeBlock(b, 80000)
//fmt.Println(gtime.Microsecond() - t1)
return
//slice := []int{1,2,3,4,5,6,7,8,9}
//index := 1
////fmt.Println(append(slice[:index], slice[index+1:]...))
//
////rear:=append([]int{}, slice[index:]...)
////slice=append(slice[0:index], 88)
////slice=append(slice, rear...)
////
////fmt.Println(slice)
//
//fmt.Println(append(append(slice[0 : index], 88), append([]int{}, slice[index : ]...)...))
//return
//a := gbinary.EncodeBits(nil, 100, 10)
//fmt.Println(a)
//b := gbinary.EncodeBitsToBytes(a)
//fmt.Println(b)
//fmt.Println(gbinary.EncodeInt32(1))
//return
//return
//fmt.Println(gbinary.DecodeToInt64([]byte{1}))
//return
//fmt.Println(gbinary.EncodeInt32(1)[0:3])
//b := []int{1,2,3}
//c := []int{4}
//copy(b[1:], c)
//fmt.Println(b)
//return
//space, err := gfilespace.New("/tmp/test")
//if err != nil {
// fmt.Println(err)
//}
//for i := 0; i < 10; i++ {
// space.AddBlock(int64(i), uint32((i + 1)*10))
//}
//fmt.Println(space.GetBlock(50))
//return
//db.Set([]byte("1"), []byte(grand.RandStr(10)))
//grand.RandStr(10)
//db.Set([]byte("r88U89b6Vv"), []byte("john211111111111111111111111"))
//db.Get([]byte("name2"))
//fmt.Println(e)
//fmt.Println(string(v))
//r := int32(binary.LittleEndian.Uint32(b))
//fmt.Println(int32(r))
//binary.BigEndian.Uint16(b)
//gbinary.DecodeToInt32([]byte{1,2,3,4})
//fmt.Println(gtime.Microsecond() - t1)
//fmt.Println([]byte{byte(i)})
//b := make([]byte, 0)
//a := ghash.BKDRHash([]byte("john"))
//for i := 0; i < 1000; i++ {
// r, e := gbinary.Encode([]byte("key_" + strconv.Itoa(i)), a, a)
// if e != nil {
// fmt.Println(e)
// return
// }
// b = append(b, r...)
//}
//fmt.Printf("length: %d\n", len(b)/1024)
//fmt.Printf("compressed: %d\n", len(gcompress.Zlib(b))/1024)
//t1 := gtime.Microsecond()
////gcompress.Zlib(b)
//gbinary.Encode([]byte("key_" + strconv.Itoa(100)), a, a)
//fmt.Println(gtime.Microsecond() - t1)
//return
//t1 := gtime.Second()
//m := make(map[uint64]bool)
//c := 0
//for i := 0; i < 100000000; i++ {
// key := ghash.SDBMHash64([]byte("this is test key" + strconv.Itoa(i)))
// if _, ok := m[key]; ok {
// c++
// } else {
// m[key] = true
// }
//}
//fmt.Println(gtime.Second() - t1)
//fmt.Println("conflicts:", c)
//fmt.Println(ghash.BKDRHash([]byte("johnWRWEREWREWRWEREWRRRRRRRRRRRRRRRRRRRRRRRRRRRRR")))
//t1 := gtime.Microsecond()
//ghash.BKDRHash64([]byte("john"))
////fmt.Println(ghash.ELFHash([]byte("john")))
////fmt.Println(ghash.JSHash([]byte("john")))
//fmt.Println(gtime.Microsecond() - t1)
//fmt.Println(ghash.BKDRHash64([]byte("john29384723894723894789sdkjfhsjkdh")))
//return
//btree := gbtree.New(3)
//t1 := gtime.Microsecond()
//for i := 1; i <= 11; i++ {
// btree.Set([]byte{byte(i)}, []byte{byte(i)})
//}
//fmt.Println(gtime.Microsecond() - t1)
//btree.Print()
//fmt.Println()
//fmt.Println()
//btree.Remove([]byte{11})
//btree.Print()
//t2 := gtime.Microsecond()
//btree.Get([]byte("key2"))
//fmt.Println(btree.Get([]byte{200}))
//fmt.Println(gtime.Microsecond() - t2)
//return
////m := gmap.NewStringInterfaceMap()
//t1 := gtime.Microsecond()
//gcrc32.EncodeString("123")
//fmt.Println(gtime.Microsecond() - t1)
//return
//db, err := leveldb.OpenFile("/tmp/lv.db", nil)
//fmt.Println(err)
//defer db.Close()
//t1 := gtime.Microsecond()
//size := 10000000
//for i := 0; i < size; i++ {
// //r := []byte(grand.RandStr(10))
// //if err := db.Set(r, r); err != nil {
// t3 := gtime.Microsecond()
// if err := db.Put([]byte("key1_" + strconv.Itoa(i)), []byte("value1_" + strconv.Itoa(i)), nil); err != nil {
// //if err := db.Set(gbinary.EncodeInt32(int32(i)), gbinary.EncodeInt32(int32(i))); err != nil {
// fmt.Println(err)
// }
// t4 := gtime.Microsecond()
// if t4 - t3 > 1000 {
// fmt.Println(t4-t3)
// }
//}
//for i := 0; i < size; i++ {
// //r := []byte(grand.RandStr(10))
// //if err := db.Set(r, r); err != nil {
// t3 := gtime.Microsecond()
// v, err := db.Get([]byte("key1_" + strconv.Itoa(i)), nil)
// if err != nil {
// //if err := db.Set(gbinary.EncodeInt32(int32(i)), gbinary.EncodeInt32(int32(i))); err != nil {
// fmt.Println(err)
// }
// if len(v) == 0 {
// fmt.Println("none")
// }
// t4 := gtime.Microsecond()
// if t4 - t3 > 1000 {
// fmt.Println(t4-t3)
// }
//}
//fmt.Println(gtime.Microsecond() - t1)
//
//return
//db, err := bolt.Open("/tmp/my.db", 0600, nil)
//if err != nil {
// log.Fatal(err)
//}
//defer db.Close()
//
//tx, err := db.Begin(true)
//if err != nil {
// log.Fatal(err)
//}
//defer tx.Rollback()
// Use the transaction...
//_, err = tx.CreateBucket([]byte("MyBucket"))
//if err != nil {
// log.Fatal(err)
//}
// Commit the transaction and check for error.
//if err := tx.Commit(); err != nil {
// log.Fatal(err)
//}
//t1 := gtime.Microsecond()
//db.Update(func(tx *bolt.Tx) error {
// b := tx.Bucket([]byte("MyBucket"))
// err := b.Put([]byte("answer"), []byte("11"))
// return err
//})
//fmt.Println(gtime.Microsecond() - t1)
//
//t2 := gtime.Microsecond()
//db.View(func(tx *bolt.Tx) error {
// b := tx.Bucket([]byte("MyBucket"))
// v := b.Get([]byte("answer"))
// fmt.Printf("The answer is: %s\n", v)
// return nil
//})
//fmt.Println(gtime.Microsecond() - t2)
//return
////db, err := gkvdb.New("/tmp/test2", "t")
//fmt.Println(err)
////fmt.Println(db.Set("1", []byte("1")))
//t1 := gtime.Microsecond()
////fmt.Println(db.Get("1"))
//fmt.Println(db.Set("1", []byte("1")))
//fmt.Println(gtime.Microsecond() - t1)
////fmt.Println(db.Set("name", []byte("222")))
//return
//for i := 0; i < 10000000; i++ {
// gfile.PutContentsAppend("/tmp/test", "1234567890")
//}
//
//file, _ := gfile.OpenWithFlag("/tmp/test", os.O_RDWR|os.O_CREATE)
//fmt.Println(gfile.GetBinContentByTwoOffsets(file, 100, 110))
////n, err := file.WriteAt([]byte("123"), 2286 445 522*8)
////n, err := file.WriteAt([]byte("123"), 1000000*(16))
////fmt.Println(n)
////fmt.Println(err)
//defer file.Close()
//fmt.Println(gcrc32.EncodeString("123"))
}

View File

@ -0,0 +1,37 @@
package main
import (
"testing"
"g/core/types/gmap"
)
func BenchmarkSet1(b *testing.B) {
a := gmap.NewStringStringMap()
m1 := make(map[string]string)
m2 := make(map[string]string)
for i := 0; i < 1000000; i ++ {
m1[string(i)] = string(i)
}
for i := 0; i < 1000000; i ++ {
m2[string(i)] = string(i) + "_2"
}
a.BatchSet(m1)
a.BatchSet(m2)
}
func BenchmarkSet2 (b *testing.B) {
a := gmap.NewStringStringMap()
m1 := make(map[string]string)
m2 := make(map[string]string)
for i := 0; i < 1000000; i ++ {
m1[string(i)] = string(i)
}
for i := 0; i < 1000000; i ++ {
m2[string(i)] = string(i) + "_2"
}
a.BatchSet2(m1)
a.BatchSet2(m2)
}

44
gexample/types/gbtree.go Normal file
View File

@ -0,0 +1,44 @@
package main
import (
"g/core/types/gbtree"
"fmt"
)
type Block struct {
index int // 文件偏移量
size uint // 区块大小(byte)
}
func (block *Block) Less(item gbtree.Item) bool {
if block.index < item.(*Block).index {
return true
}
return false
}
func main () {
tr := gbtree.New(10)
//t1 := gtime.Microsecond()
for i := 0; i < 10; i++ {
tr.ReplaceOrInsert(&Block{i, uint(i*10)})
}
//fmt.Println("create", gtime.Microsecond() - t1)
//t2 := gtime.Microsecond()
//b := &Block{9, 10}
//fmt.Println(tr.Get(b))
//fmt.Println(tr.Delete(b))
//fmt.Println(tr.Get(b))
//fmt.Println("get", gtime.Microsecond() - t2)
//t3 := gtime.Microsecond()
//var b Block
tr.AscendGreaterOrEqual(&Block{2, 0}, func(item gbtree.Item) bool {
fmt.Println(item)
return true
})
//fmt.Println("asc fetch", gtime.Microsecond() - t3, b)
}

View File