1// Copyright 2014 Richard Lehane. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package siegreader
16
17import (
18	"io"
19	"os"
20)
21
22// Buffers is a combined pool of stream, external and file buffers
23type Buffers struct {
24	spool *pool // Pool of stream Buffers
25	fpool *pool // Pool of file Buffers
26	epool *pool // Pool of external buffers
27
28	fdatas *datas // file datas
29}
30
31// New creates a new pool of stream, external and file buffers
32func New() *Buffers {
33	return &Buffers{
34		spool: newPool(newStream),
35		fpool: newPool(newFile),
36		epool: newPool(newExternal),
37		fdatas: &datas{
38			newPool(newBigFile),
39			newPool(newSmallFile),
40			newPool(newMmap),
41		},
42	}
43}
44
45// Get returns a Buffer reading from the provided io.Reader.
46// Get returns a Buffer backed by a stream, external or file
47// source buffer depending on the type of reader.
48// Source buffers are re-cycled where possible.
49func (b *Buffers) Get(src io.Reader) (*Buffer, error) {
50	f, ok := src.(*os.File)
51	if ok {
52		stat, err := f.Stat()
53		if err != nil || stat.Mode()&os.ModeType != 0 {
54			ok = false
55		}
56	}
57	if !ok {
58		e, ok := src.(source)
59		if !ok || !e.IsSlicer() {
60			stream := b.spool.get().(*stream)
61			buf := &Buffer{}
62			err := stream.setSource(src, buf)
63			buf.bufferSrc = stream
64			return buf, err
65		}
66		ext := b.epool.get().(*external)
67		err := ext.setSource(e)
68		return &Buffer{bufferSrc: ext}, err
69	}
70	fbuf := b.fpool.get().(*file)
71	err := fbuf.setSource(f, b.fdatas)
72	return &Buffer{bufferSrc: fbuf}, err
73}
74
75// Put returns a Buffer to the pool for re-cycling.
76func (b *Buffers) Put(i *Buffer) {
77	switch v := i.bufferSrc.(type) {
78	default:
79		panic("Siegreader: unknown buffer type")
80	case *stream:
81		v.cleanUp()
82		b.spool.put(v)
83	case *file:
84		b.fdatas.put(v.data)
85		b.fpool.put(v)
86	case *external:
87		b.epool.put(v)
88	}
89}
90
91// data pool (used by file)
92// pool of big files, small files, and mmap files
93type datas struct {
94	bfpool *pool
95	sfpool *pool
96	mpool  *pool
97}
98
99func (d *datas) get(f *file) data {
100	if mmapable(f.sz) {
101		m := d.mpool.get().(*mmap)
102		if err := m.setSource(f); err == nil {
103			return m
104		}
105		d.mpool.put(m) // replace on error and get big file instead
106	}
107	if f.sz <= int64(smallFileSz) {
108		sf := d.sfpool.get().(*smallfile)
109		sf.setSource(f)
110		return sf
111	}
112	bf := d.bfpool.get().(*bigfile)
113	bf.setSource(f)
114	return bf
115}
116
117func (d *datas) put(i data) {
118	if i == nil {
119		return
120	}
121	switch v := i.(type) {
122	default:
123		panic("Siegreader: unknown data type")
124	case *bigfile:
125		d.bfpool.put(v)
126	case *smallfile:
127		d.sfpool.put(v)
128	case *mmap:
129		v.reset()
130		d.mpool.put(v)
131	}
132	return
133}
134