github.com/v2fly/tools@v0.100.0/godoc/util/util.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package util contains utility types and functions for godoc.
     6  package util // import "github.com/v2fly/tools/godoc/util"
     7  
     8  import (
     9  	pathpkg "path"
    10  	"sync"
    11  	"time"
    12  	"unicode/utf8"
    13  
    14  	"github.com/v2fly/tools/godoc/vfs"
    15  )
    16  
    17  // An RWValue wraps a value and permits mutually exclusive
    18  // access to it and records the time the value was last set.
    19  type RWValue struct {
    20  	mutex     sync.RWMutex
    21  	value     interface{}
    22  	timestamp time.Time // time of last set()
    23  }
    24  
    25  func (v *RWValue) Set(value interface{}) {
    26  	v.mutex.Lock()
    27  	v.value = value
    28  	v.timestamp = time.Now()
    29  	v.mutex.Unlock()
    30  }
    31  
    32  func (v *RWValue) Get() (interface{}, time.Time) {
    33  	v.mutex.RLock()
    34  	defer v.mutex.RUnlock()
    35  	return v.value, v.timestamp
    36  }
    37  
    38  // IsText reports whether a significant prefix of s looks like correct UTF-8;
    39  // that is, if it is likely that s is human-readable text.
    40  func IsText(s []byte) bool {
    41  	const max = 1024 // at least utf8.UTFMax
    42  	if len(s) > max {
    43  		s = s[0:max]
    44  	}
    45  	for i, c := range string(s) {
    46  		if i+utf8.UTFMax > len(s) {
    47  			// last char may be incomplete - ignore
    48  			break
    49  		}
    50  		if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' {
    51  			// decoding error or control character - not a text file
    52  			return false
    53  		}
    54  	}
    55  	return true
    56  }
    57  
    58  // textExt[x] is true if the extension x indicates a text file, and false otherwise.
    59  var textExt = map[string]bool{
    60  	".css": false, // must be served raw
    61  	".js":  false, // must be served raw
    62  	".svg": false, // must be served raw
    63  }
    64  
    65  // IsTextFile reports whether the file has a known extension indicating
    66  // a text file, or if a significant chunk of the specified file looks like
    67  // correct UTF-8; that is, if it is likely that the file contains human-
    68  // readable text.
    69  func IsTextFile(fs vfs.Opener, filename string) bool {
    70  	// if the extension is known, use it for decision making
    71  	if isText, found := textExt[pathpkg.Ext(filename)]; found {
    72  		return isText
    73  	}
    74  
    75  	// the extension is not known; read an initial chunk
    76  	// of the file and check if it looks like text
    77  	f, err := fs.Open(filename)
    78  	if err != nil {
    79  		return false
    80  	}
    81  	defer f.Close()
    82  
    83  	var buf [1024]byte
    84  	n, err := f.Read(buf[0:])
    85  	if err != nil {
    86  		return false
    87  	}
    88  
    89  	return IsText(buf[0:n])
    90  }