github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/swarm/network/stream/intervals/intervals.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package intervals
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"strconv"
    23  	"sync"
    24  )
    25  
    26  // Intervals store a list of intervals. Its purpose is to provide
    27  // methods to add new intervals and retrieve missing intervals that
    28  // need to be added.
    29  // It may be used in synchronization of streaming data to persist
    30  // retrieved data ranges between sessions.
    31  type Intervals struct {
    32  	start  uint64
    33  	ranges [][2]uint64
    34  	mu     sync.RWMutex
    35  }
    36  
    37  // New creates a new instance of Intervals.
    38  // Start argument limits the lower bound of intervals.
    39  // No range bellow start bound will be added by Add method or
    40  // returned by Next method. This limit may be used for
    41  // tracking "live" synchronization, where the sync session
    42  // starts from a specific value, and if "live" sync intervals
    43  // need to be merged with historical ones, it can be safely done.
    44  func NewIntervals(start uint64) *Intervals {
    45  	return &Intervals{
    46  		start: start,
    47  	}
    48  }
    49  
    50  // Add adds a new range to intervals. Range start and end are values
    51  // are both inclusive.
    52  func (i *Intervals) Add(start, end uint64) {
    53  	i.mu.Lock()
    54  	defer i.mu.Unlock()
    55  
    56  	i.add(start, end)
    57  }
    58  
    59  func (i *Intervals) add(start, end uint64) {
    60  	if start < i.start {
    61  		start = i.start
    62  	}
    63  	if end < i.start {
    64  		return
    65  	}
    66  	minStartJ := -1
    67  	maxEndJ := -1
    68  	j := 0
    69  	for ; j < len(i.ranges); j++ {
    70  		if minStartJ < 0 {
    71  			if (start <= i.ranges[j][0] && end+1 >= i.ranges[j][0]) || (start <= i.ranges[j][1]+1 && end+1 >= i.ranges[j][1]) {
    72  				if i.ranges[j][0] < start {
    73  					start = i.ranges[j][0]
    74  				}
    75  				minStartJ = j
    76  			}
    77  		}
    78  		if (start <= i.ranges[j][1] && end+1 >= i.ranges[j][1]) || (start <= i.ranges[j][0] && end+1 >= i.ranges[j][0]) {
    79  			if i.ranges[j][1] > end {
    80  				end = i.ranges[j][1]
    81  			}
    82  			maxEndJ = j
    83  		}
    84  		if end+1 <= i.ranges[j][0] {
    85  			break
    86  		}
    87  	}
    88  	if minStartJ < 0 && maxEndJ < 0 {
    89  		i.ranges = append(i.ranges[:j], append([][2]uint64{{start, end}}, i.ranges[j:]...)...)
    90  		return
    91  	}
    92  	if minStartJ >= 0 {
    93  		i.ranges[minStartJ][0] = start
    94  	}
    95  	if maxEndJ >= 0 {
    96  		i.ranges[maxEndJ][1] = end
    97  	}
    98  	if minStartJ >= 0 && maxEndJ >= 0 && minStartJ != maxEndJ {
    99  		i.ranges[maxEndJ][0] = start
   100  		i.ranges = append(i.ranges[:minStartJ], i.ranges[maxEndJ:]...)
   101  	}
   102  }
   103  
   104  // Merge adds all the intervals from the m Interval to current one.
   105  func (i *Intervals) Merge(m *Intervals) {
   106  	m.mu.RLock()
   107  	defer m.mu.RUnlock()
   108  	i.mu.Lock()
   109  	defer i.mu.Unlock()
   110  
   111  	for _, r := range m.ranges {
   112  		i.add(r[0], r[1])
   113  	}
   114  }
   115  
   116  // Next returns the first range interval that is not fulfilled. Returned
   117  // start and end values are both inclusive, meaning that the whole range
   118  // including start and end need to be added in order to full the gap
   119  // in intervals.
   120  // Returned value for end is 0 if the next interval is after the whole
   121  // range that is stored in Intervals. Zero end value represents no limit
   122  // on the next interval length.
   123  func (i *Intervals) Next() (start, end uint64) {
   124  	i.mu.RLock()
   125  	defer i.mu.RUnlock()
   126  
   127  	l := len(i.ranges)
   128  	if l == 0 {
   129  		return i.start, 0
   130  	}
   131  	if i.ranges[0][0] != i.start {
   132  		return i.start, i.ranges[0][0] - 1
   133  	}
   134  	if l == 1 {
   135  		return i.ranges[0][1] + 1, 0
   136  	}
   137  	return i.ranges[0][1] + 1, i.ranges[1][0] - 1
   138  }
   139  
   140  // Last returns the value that is at the end of the last interval.
   141  func (i *Intervals) Last() (end uint64) {
   142  	i.mu.RLock()
   143  	defer i.mu.RUnlock()
   144  
   145  	l := len(i.ranges)
   146  	if l == 0 {
   147  		return 0
   148  	}
   149  	return i.ranges[l-1][1]
   150  }
   151  
   152  // String returns a descriptive representation of range intervals
   153  // in [] notation, as a list of two element vectors.
   154  func (i *Intervals) String() string {
   155  	return fmt.Sprint(i.ranges)
   156  }
   157  
   158  // MarshalBinary encodes Intervals parameters into a semicolon separated list.
   159  // The first element in the list is base36-encoded start value. The following
   160  // elements are two base36-encoded value ranges separated by comma.
   161  func (i *Intervals) MarshalBinary() (data []byte, err error) {
   162  	d := make([][]byte, len(i.ranges)+1)
   163  	d[0] = []byte(strconv.FormatUint(i.start, 36))
   164  	for j := range i.ranges {
   165  		r := i.ranges[j]
   166  		d[j+1] = []byte(strconv.FormatUint(r[0], 36) + "," + strconv.FormatUint(r[1], 36))
   167  	}
   168  	return bytes.Join(d, []byte(";")), nil
   169  }
   170  
   171  // UnmarshalBinary decodes data according to the Intervals.MarshalBinary format.
   172  func (i *Intervals) UnmarshalBinary(data []byte) (err error) {
   173  	d := bytes.Split(data, []byte(";"))
   174  	l := len(d)
   175  	if l == 0 {
   176  		return nil
   177  	}
   178  	if l >= 1 {
   179  		i.start, err = strconv.ParseUint(string(d[0]), 36, 64)
   180  		if err != nil {
   181  			return err
   182  		}
   183  	}
   184  	if l == 1 {
   185  		return nil
   186  	}
   187  
   188  	i.ranges = make([][2]uint64, 0, l-1)
   189  	for j := 1; j < l; j++ {
   190  		r := bytes.SplitN(d[j], []byte(","), 2)
   191  		if len(r) < 2 {
   192  			return fmt.Errorf("range %d has less then 2 elements", j)
   193  		}
   194  		start, err := strconv.ParseUint(string(r[0]), 36, 64)
   195  		if err != nil {
   196  			return fmt.Errorf("parsing the first element in range %d: %v", j, err)
   197  		}
   198  		end, err := strconv.ParseUint(string(r[1]), 36, 64)
   199  		if err != nil {
   200  			return fmt.Errorf("parsing the second element in range %d: %v", j, err)
   201  		}
   202  		i.ranges = append(i.ranges, [2]uint64{start, end})
   203  	}
   204  
   205  	return nil
   206  }