github.com/influx6/npkg@v0.8.8/nbytes/pbytes/pbytes.go (about)

     1  package pbytes
     2  
     3  import (
     4  	"bytes"
     5  	"sync"
     6  )
     7  
     8  type bufferHandler interface {
     9  	Put(*Buffer)
    10  }
    11  
    12  //************************************************************************
    13  // Buffer
    14  //************************************************************************
    15  
    16  // Buffer holds a allocated memory which will be used by
    17  // receiver and discard once done with byte slice to
    18  // allow re-use.
    19  type Buffer struct {
    20  	bit  int
    21  	Data []byte
    22  	pool bufferHandler
    23  }
    24  
    25  func (b *Buffer) Discard() {
    26  	b.pool.Put(b)
    27  }
    28  
    29  //************************************************************************
    30  // BitsBoot
    31  //************************************************************************
    32  
    33  type bitsBoot struct {
    34  	max  int
    35  	pl   sync.Mutex
    36  	free []*Buffer
    37  }
    38  
    39  func (b *bitsBoot) Put(br *Buffer) {
    40  	b.pl.Lock()
    41  	defer b.pl.Unlock()
    42  
    43  	br.pool = nil
    44  	b.free = append(b.free, br)
    45  }
    46  
    47  func (b *bitsBoot) Get(n int) *Buffer {
    48  	b.pl.Lock()
    49  	defer b.pl.Unlock()
    50  
    51  	free := len(b.free)
    52  	if free == 0 {
    53  		mem := make([]byte, b.max)
    54  		br := &Buffer{
    55  			pool: b,
    56  			Data: mem[:n],
    57  		}
    58  		return br
    59  	}
    60  
    61  	item := b.free[0]
    62  	item.pool = b
    63  
    64  	if free == 0 {
    65  		b.free = b.free[:0]
    66  		return item
    67  	}
    68  
    69  	b.free = b.free[1:]
    70  	return item
    71  }
    72  
    73  // BitsBoot implements a custom pool for issue Buffers,
    74  // but uses a internal []Buffer slice instead of the sync.Pool.
    75  type BitsBoot struct {
    76  	distance int
    77  	pl       sync.Mutex
    78  	pools    []*bitsBoot
    79  	indexes  map[int]int
    80  }
    81  
    82  // NewBitsBoot returns a new instance of a BitsBoot which returns
    83  // allocated and managed Buffer.
    84  func NewBitsBoot(distance int, initialAmount int) *BitsBoot {
    85  	initials := make([]*bitsBoot, 0)
    86  	indexes := make(map[int]int)
    87  
    88  	for i := 1; i <= initialAmount; i++ {
    89  		sizeDist := distance * i
    90  
    91  		indexes[sizeDist] = len(initials)
    92  		initials = append(initials, &bitsBoot{
    93  			max:  sizeDist,
    94  			free: make([]*Buffer, 0, 100),
    95  		})
    96  	}
    97  
    98  	return &BitsBoot{
    99  		indexes:  indexes,
   100  		distance: distance,
   101  		pools:    initials,
   102  	}
   103  }
   104  
   105  // Put returns the []byte by using the capacity of the slice to find its pool.
   106  func (bp *BitsBoot) Put(bu *Buffer) {
   107  	bu.pool = nil
   108  
   109  	bp.pl.Lock()
   110  	index, ok := bp.indexes[bu.bit]
   111  	if !ok {
   112  		bp.pl.Unlock()
   113  		return
   114  	}
   115  	pool := bp.pools[index]
   116  	bp.pl.Unlock()
   117  
   118  	pool.Put(bu)
   119  }
   120  
   121  // Get returns a new or existing []byte from it's internal size RangePool.
   122  // It gets a RangePool or creates one if non exists for the size + it's distance value
   123  // then gets a []byte from that RangePool.
   124  func (bp *BitsBoot) Get(size int) *Buffer {
   125  	bp.pl.Lock()
   126  
   127  	if poolIndex, ok := bp.indexes[size]; ok {
   128  		pool := bp.pools[poolIndex]
   129  		bp.pl.Unlock()
   130  
   131  		return pool.Get(size)
   132  	}
   133  
   134  	// loop through RangePool till we find the distance where size is no more
   135  	// greater, which means that pool will be suitable as the size provider for
   136  	// this size need.
   137  	for _, pool := range bp.pools {
   138  		if pool.max < size {
   139  			continue
   140  		}
   141  
   142  		bp.pl.Unlock()
   143  		return pool.Get(size)
   144  	}
   145  
   146  	// We dont have any pool within size range, so create new RangePool suited for this size.
   147  	newDistance := ((size / bp.distance) + 1) * bp.distance
   148  	newPool := &bitsBoot{
   149  		max:  newDistance,
   150  		free: make([]*Buffer, 0, 100),
   151  	}
   152  
   153  	bp.indexes[newDistance] = len(bp.pools)
   154  	bp.pools = append(bp.pools, newPool)
   155  	bp.pl.Unlock()
   156  
   157  	return newPool.Get(size)
   158  }
   159  
   160  //************************************************************************
   161  // BitsBoot
   162  //************************************************************************
   163  
   164  type bitsPool struct {
   165  	max    int
   166  	pool   *sync.Pool
   167  	source bufferHandler
   168  }
   169  
   170  func (b *bitsPool) Put(bu *Buffer) {
   171  	bu.pool = nil
   172  	b.pool.Put(bu)
   173  }
   174  
   175  func (b *bitsPool) Get(n int) *Buffer {
   176  	br := b.pool.Get().(*Buffer)
   177  	br.Data = br.Data[:n]
   178  	br.pool = b
   179  	return br
   180  }
   181  
   182  // BitsPool implements a custom pool for issue Buffers,
   183  // using the sync.Pool.
   184  type BitsPool struct {
   185  	distance int
   186  	pl       sync.Mutex
   187  	pools    []*bitsPool
   188  	indexes  map[int]int
   189  }
   190  
   191  // NewBitsPool returns a new instance of a BitsPool which returns
   192  // allocated and managed Buffer.
   193  func NewBitsPool(distance int, initialAmount int) *BitsPool {
   194  	initials := make([]*bitsPool, 0)
   195  	indexes := make(map[int]int)
   196  
   197  	for i := 1; i <= initialAmount; i++ {
   198  		sizeDist := distance * i
   199  
   200  		indexes[sizeDist] = len(initials)
   201  		initials = append(initials, &bitsPool{
   202  			max: sizeDist,
   203  			pool: &sync.Pool{
   204  				New: func() interface{} {
   205  					return &Buffer{
   206  						bit:  sizeDist,
   207  						Data: make([]byte, sizeDist),
   208  					}
   209  				},
   210  			},
   211  		})
   212  	}
   213  
   214  	return &BitsPool{
   215  		indexes:  indexes,
   216  		distance: distance,
   217  		pools:    initials,
   218  	}
   219  }
   220  
   221  // Get returns a new or existing []byte from it's internal size RangePool.
   222  // It gets a RangePool or creates one if non exists for the size + it's distance value
   223  // then gets a []byte from that RangePool.
   224  func (bp *BitsPool) Get(size int) *Buffer {
   225  	bp.pl.Lock()
   226  	defer bp.pl.Unlock()
   227  
   228  	if poolIndex, ok := bp.indexes[size]; ok {
   229  		pool := bp.pools[poolIndex]
   230  		return pool.Get(size)
   231  	}
   232  
   233  	// loop through RangePool till we find the distance where size is no more
   234  	// greater, which means that pool will be suitable as the size provider for
   235  	// this size need.
   236  	for _, pool := range bp.pools {
   237  		if pool.max < size {
   238  			continue
   239  		}
   240  
   241  		return pool.Get(size)
   242  	}
   243  
   244  	// We dont have any pool within size range, so create new RangePool suited for this size.
   245  	newDistance := ((size / bp.distance) + 1) * bp.distance
   246  	newPool := &bitsPool{
   247  		max: newDistance,
   248  		pool: &sync.Pool{
   249  			New: func() interface{} {
   250  				return &Buffer{
   251  					bit:  newDistance,
   252  					Data: make([]byte, newDistance),
   253  				}
   254  			},
   255  		},
   256  	}
   257  
   258  	bp.indexes[newDistance] = len(bp.pools)
   259  	bp.pools = append(bp.pools, newPool)
   260  	return newPool.Get(size)
   261  }
   262  
   263  //************************************************************************
   264  // BytesPool
   265  //************************************************************************
   266  
   267  type rangePool struct {
   268  	max  int
   269  	pool *sync.Pool
   270  }
   271  
   272  // BytesPool exists to contain multiple RangePool that lies within giving distance range.
   273  // It creates a internal array of BytesPool which are distanced between each other by
   274  // provided distance. Whenever giving call to get a []byte for a giving size is
   275  // within existing pool distances, it calls that RangePool responsible for that size and
   276  // retrieves giving []byte from that pool. If no range as such exists, it creates
   277  // a new RangePool for the size + BytesPool.Distance set an instantiation, then retrieves
   278  // a []byte from that.
   279  type BytesPool struct {
   280  	distance int
   281  	pl       sync.Mutex
   282  	pools    []*rangePool
   283  	indexes  map[int]int
   284  }
   285  
   286  // NewBytesPool returns a new instance of a BytesPool with size distance used for new npools
   287  // and creates as many as the initialAmount of RangePools internally to service those size
   288  // requests.
   289  func NewBytesPool(distance int, initialAmount int) *BytesPool {
   290  	initials := make([]*rangePool, 0)
   291  	indexes := make(map[int]int)
   292  
   293  	for i := 1; i <= initialAmount; i++ {
   294  		sizeDist := distance * i
   295  
   296  		indexes[sizeDist] = len(initials)
   297  		initials = append(initials, &rangePool{
   298  			max: sizeDist,
   299  			pool: &sync.Pool{
   300  				New: func() interface{} {
   301  					return bytes.NewBuffer(make([]byte, 0, sizeDist))
   302  				},
   303  			},
   304  		})
   305  	}
   306  
   307  	return &BytesPool{
   308  		distance: distance,
   309  		pools:    initials,
   310  		indexes:  indexes,
   311  	}
   312  }
   313  
   314  // Put returns the []byte by using the capacity of the slice to find its pool.
   315  func (bp *BytesPool) Put(bu *bytes.Buffer) {
   316  	bp.pl.Lock()
   317  	defer bp.pl.Unlock()
   318  
   319  	if index, ok := bp.indexes[bu.Cap()]; ok {
   320  		pool := bp.pools[index]
   321  		pool.pool.Put(bu)
   322  	}
   323  }
   324  
   325  // Get returns a new or existing []byte from it's internal size RangePool.
   326  // It gets a RangePool or creates one if non exists for the size + it's distance value
   327  // then gets a []byte from that RangePool.
   328  func (bp *BytesPool) Get(size int) *bytes.Buffer {
   329  	bp.pl.Lock()
   330  	defer bp.pl.Unlock()
   331  
   332  	// loop through RangePool till we find the distance where size is no more
   333  	// greater, which means that pool will be suitable as the size provider for
   334  	// this size need.
   335  	for _, pool := range bp.pools {
   336  		if pool.max < size {
   337  			continue
   338  		}
   339  
   340  		return pool.pool.Get().(*bytes.Buffer)
   341  	}
   342  
   343  	// We dont have any pool within size range, so create new RangePool suited for this size.
   344  	newDistance := ((size / bp.distance) + 1) * bp.distance
   345  	newPool := &rangePool{
   346  		max: newDistance,
   347  		pool: &sync.Pool{
   348  			New: func() interface{} {
   349  				return bytes.NewBuffer(make([]byte, 0, newDistance))
   350  			},
   351  		},
   352  	}
   353  
   354  	bp.indexes[newDistance] = len(bp.pools)
   355  	bp.pools = append(bp.pools, newPool)
   356  
   357  	return newPool.pool.Get().(*bytes.Buffer)
   358  }
   359  
   360  //************************************************************************
   361  // BytePool
   362  //************************************************************************
   363  
   364  // BytePool implements a leaky pool of []byte in the form of a bounded
   365  // channel.
   366  type BytePool struct {
   367  	c chan []byte
   368  	w int
   369  }
   370  
   371  // NewBytePool creates a new BytePool bounded to the given maxSize, with new
   372  // byte arrays sized based on width.
   373  func NewBytePool(maxSize int, width int) (bp *BytePool) {
   374  	return &BytePool{
   375  		c: make(chan []byte, maxSize),
   376  		w: width,
   377  	}
   378  }
   379  
   380  // Get gets a []byte from the BytePool, or creates a new one if none are
   381  // available in the pool.
   382  func (bp *BytePool) Get() (b []byte) {
   383  	select {
   384  	case b = <-bp.c:
   385  		// reuse existing buffer
   386  	default:
   387  		// create new buffer
   388  		b = make([]byte, bp.w)
   389  	}
   390  	return
   391  }
   392  
   393  // Put returns the given Buffer to the BytePool.
   394  func (bp *BytePool) Put(b []byte) {
   395  	select {
   396  	case bp.c <- b:
   397  		// buffer went back into pool
   398  	default:
   399  		// buffer didn't go back into pool, just discard
   400  	}
   401  }
   402  
   403  // Width returns the width of the byte arrays in this pool.
   404  func (bp *BytePool) Width() (n int) {
   405  	return bp.w
   406  }