Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  hash_provider.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2023 Google Inc. All rights reserved.
//
// 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.

package proptools

import (
 "cmp"
 "encoding/binary"
 "encoding/json"
 "fmt"
 "hash"
 "hash/fnv"
 "math"
 "reflect"
 "slices"
 "sort"
 "strconv"
 "text/scanner"
 "unsafe"

 "github.com/google/blueprint/pool"
)

var hasherPool = pool.New[Hasher]()

type CustomHash interface {
 CustomHash(hasher *Hasher) error
}

const HashSize = 8

type Hash [1]uint64

func (h *Hash) UnmarshalJSON(bytes []byte) error {
 var s []uint64
 err := json.Unmarshal(bytes, &s)
 if err != nil {
  return err
 }
 if len(s) != len(h) {
  return fmt.Errorf("expected %d elements, got %d", len(h), len(s))
 }
 copy(h[:], s)
 return nil
}

func (h *Hash) MarshalJSON() ([]byte, error) {
 return json.Marshal(h[:])
}

var ZeroHash Hash

func (h *Hash) FormatUint(base int) string {
 return strconv.FormatUint(h[0], base)
}

func (h *Hash) PutBigEndian(buf []byte) {
 binary.BigEndian.PutUint64(buf, h[0])
}

func (h *Hash) Bytes() []byte {
 ptr := unsafe.Pointer(unsafe.SliceData(h[:]))
 return unsafe.Slice((*byte)(ptr), len(h)*int(unsafe.Sizeof(h[0])))
}

// CalculateHashReflection calculates the hash of any value, using the CustomHash
// interface if the type implements it, or falling back to reflection if it does not.
func CalculateHashReflection(value any) (Hash, error) {
 if value == nil {
  return ZeroHash, nil
 }
 if ch, ok := value.(CustomHash); ok {
  return CalculateHash(ch)
 }

 hasher := hasherPool.Get()
 defer hasherPool.Put(hasher)
 hasher.reset()

 v := reflect.ValueOf(value)
 err := hasher.CalculateHashReflection(v)
 if err != nil {
  return ZeroHash, err
 }

 return Hash{hasher.Sum64()}, nil
}

// CalculateHash calculates the hash of a value that implements CustomHash.
func CalculateHash[T CustomHash](v T) (Hash, error) {
 hasher := hasherPool.Get()
 defer hasherPool.Put(hasher)
 hasher.reset()

 // We need to distinguish A from *A.
 hasher.HashType(reflect.TypeOf(v))
 err := v.CustomHash(hasher)
 if err != nil {
  return Hash{}, err
 }
 return Hash{hasher.Sum64()}, nil
}

type Hasher struct {
 hash.Hash64
 int64Buf      [8]byte
 ptrs          map[any]uint64
 visiting      map[any]bool
 mapStateCache *mapState
}

// Preallocate the ptrs map in the hasher to a value slightly larger than the maximum number of pointers
// seen in a call to CalculateHash to avoid allocations.  The hasher objects are reused in a pool, so the
// total number of these maps will be small.
const ptrsMapSize = 16384

type mapState struct {
 indexes []int
 keys    []reflect.Value
 values  []reflect.Value
}

func NewHasher() *Hasher {
 hasher := &Hasher{}
 hasher.reset()
 return hasher
}

func HashReference(hasher *Hasher, addr any, hash func(*Hasher) error) error {
 if hasher.ptrs == nil {
  hasher.ptrs = make(map[any]uint64, ptrsMapSize)
 }
 if hasher.visiting == nil {
  hasher.visiting = make(map[any]bool, ptrsMapSize)
 }
 if _, ok := hasher.visiting[addr]; ok {
  // Circular dependency detected (we have this in Scope at least), just return nil for now.
  return nil
 }

 // The special logic below is to avoid hashing the same pointer more than once.
 // We store the current hash value, then reset the hasher to a clean state in
 // order to calculate the hash of the pointer which will be cached for future
 // encounters. Once we have the hash value of the pointer, we hash both the
 // stored hash value and the hash value of the pointer.
 prevHash := hasher.Sum64()
 hasher.Reset()

 ptrHash, ok := hasher.ptrs[addr]
 if !ok {
  hasher.visiting[addr] = true
  err := hash(hasher)
  if err != nil {
   return fmt.Errorf("in pointer: %s", err.Error())
  }
  ptrHash = hasher.Sum64()
  hasher.ptrs[addr] = ptrHash
  delete(hasher.visiting, addr)
 }

 hasher.Reset()
 hasher.WriteUint64(prevHash)
 hasher.WriteUint64(ptrHash)
 return nil
}

func (hasher *Hasher) reset() {
 if hasher.Hash64 == nil {
  hasher.Hash64 = fnv.New64()
 } else {
  hasher.Hash64.Reset()
 }

 clear(hasher.ptrs)
 clear(hasher.visiting)
}

func (hasher *Hasher) WriteUint64(i uint64) {
 binary.LittleEndian.PutUint64(hasher.int64Buf[:], i)
 hasher.Write(hasher.int64Buf[:])
}

func (hasher *Hasher) WriteInt(i int) {
 hasher.WriteUint64(uint64(i))
}

func (hasher *Hasher) WriteByte(i byte) {
 hasher.int64Buf[0] = i
 hasher.Write(hasher.int64Buf[:1])
}

func (hasher *Hasher) WriteString(s string) {
 strLen := len(s)
 if strLen == 0 {
  // unsafe.StringData is unspecified in this case
  hasher.WriteByte(0)
  return
 }

 hasher.Write(unsafe.Slice(unsafe.StringData(s), strLen))
}

func (hasher *Hasher) WriteHash(h Hash) {
 for _, e := range h {
  hasher.WriteUint64(e)
 }
}

func (hasher *Hasher) getMapState(size int) *mapState {
 s := hasher.mapStateCache
 // Clear hasher.mapStateCache so that any recursive uses don't collide with this frame.
 hasher.mapStateCache = nil

 if s == nil {
  s = &mapState{}
 }

 // Reset the slices to length `size` and capacity at least `size`
 s.indexes = slices.Grow(s.indexes[:0], size)[0:size]
 s.keys = slices.Grow(s.keys[:0], size)[0:size]
 s.values = slices.Grow(s.values[:0], size)[0:size]

 return s
}

func (hasher *Hasher) putMapState(s *mapState) {
 if hasher.mapStateCache == nil || cap(hasher.mapStateCache.indexes) < cap(s.indexes) {
  hasher.mapStateCache = s
 }
}

func (hasher *Hasher) HashType(t reflect.Type) {
 var h Hash
 var err error
 h, err = TypeHash(t)
 if err != nil {
  panic(err)
 }
 hasher.WriteHash(h)
}

func (hasher *Hasher) CalculateHashReflection(v reflect.Value) error {
 // Include the hash of the type so that hashes of types with the same contents, for example
 // empty slices of different types, produce different hashes.  The hash of each type is cached,
 // so this should be very fast.
 hasher.HashType(v.Type())
 switch v.Kind() {
 case reflect.Struct:
  // The scanner.Position is intentionally excluded from the hash calculation.
  // This field should only be used for printing user-facing error messages,
  // as it is sensitive to formatting changes like comments and whitespace.
  // Including it would cause the hash to change and trigger an unnecessary
  // re-analysis when no actual property has been modified.
  if v.Type() == reflect.TypeOf(scanner.Position{}) {
   return nil
  }
  l := v.NumField()
  hasher.WriteInt(l)
  for i := 0; i < l; i++ {
   err := hasher.CalculateHashReflection(v.Field(i))
   if err != nil {
    return fmt.Errorf("in field %s: %s", v.Type().Field(i).Name, err.Error())
   }
  }
 case reflect.Map:
  l := v.Len()
  hasher.WriteInt(l)
  iter := v.MapRange()
  s := hasher.getMapState(l)
  for i := 0; iter.Next(); i++ {
   s.indexes[i] = i
   s.keys[i] = iter.Key()
   s.values[i] = iter.Value()
  }
  slices.SortFunc(s.indexes, func(i, j int) int {
   return compare_values(s.keys[i], s.keys[j])
  })
  for i := 0; i < l; i++ {
   err := hasher.CalculateHashReflection(s.keys[s.indexes[i]])
   if err != nil {
    return fmt.Errorf("in map: %s", err.Error())
   }
   err = hasher.CalculateHashReflection(s.values[s.indexes[i]])
   if err != nil {
    return fmt.Errorf("in map: %s", err.Error())
   }
  }
  hasher.putMapState(s)
 case reflect.Slice, reflect.Array:
  l := v.Len()
  hasher.WriteInt(l)
  for i := 0; i < l; i++ {
   err := hasher.CalculateHashReflection(v.Index(i))
   if err != nil {
    return fmt.Errorf("in %s at index %d: %s", v.Kind().String(), i, err.Error())
   }
  }
 case reflect.Pointer:
  if v.IsNil() {
   hasher.WriteByte(0)
   return nil
  }
  addr := v.Pointer()
  return HashReference(hasher, addr, func(hasher *Hasher) error {
   return hasher.CalculateHashReflection(v.Elem())
  })
 case reflect.Interface:
  if v.IsNil() {
   hasher.WriteByte(0)
  } else {
   var err error
   // The only way get the pointer out of an interface to hash it or check for cycles
   // would be InterfaceData(), but that's deprecated and seems like it has undefined behavior.
   if ch, ok := isCustomHash(v); ok {
    err = ch.CustomHash(hasher)
   } else {
    err = hasher.CalculateHashReflection(v.Elem())
   }
   if err != nil {
    return fmt.Errorf("in interface: %s", err.Error())
   }
  }
 case reflect.String:
  hasher.WriteString(v.String())
 case reflect.Bool:
  if v.Bool() {
   hasher.WriteByte(1)
  } else {
   hasher.WriteByte(0)
  }
 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  hasher.WriteUint64(v.Uint())
 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  hasher.WriteUint64(uint64(v.Int()))
 case reflect.Float32, reflect.Float64:
  hasher.WriteUint64(math.Float64bits(v.Float()))
 default:
  return fmt.Errorf("data may only contain primitives, strings, arrays, slices, structs, maps, and pointers, found: %s", v.Kind().String())
 }
 return nil
}

func isCustomHash(v reflect.Value) (CustomHash, bool) {
 if !v.CanInterface() {
  return nil, false
 }
 ch, ok := v.Interface().(CustomHash)
 return ch, ok
}

type Comparer[T any] interface {
 Compare(other T) int
}

// SortOrdered sorts slices of any type that is in cmp.Ordered
// (int, string, float64, etc.)
func SortOrdered[T cmp.Ordered](s []T) {
 sort.Slice(s, func(i, j int) bool {
  return s[i] < s[j]
 })
}

// SortCustom sorts slices of any type that implements Comparer interface.
func SortCustom[T Comparer[T]](s []T) {
 sort.Slice(s, func(i, j int) bool {
  return s[i].Compare(s[j]) < 0
 })
}

func compare_values(x, y reflect.Value) int {
 if x.Type() != y.Type() {
  panic("Expected equal types")
 }

 switch x.Kind() {
 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  return cmp.Compare(x.Uint(), y.Uint())
 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  return cmp.Compare(x.Int(), y.Int())
 case reflect.Float32, reflect.Float64:
  return cmp.Compare(x.Float(), y.Float())
 case reflect.String:
  return cmp.Compare(x.String(), y.String())
 case reflect.Bool:
  if x.Bool() == y.Bool() {
   return 0
  } else if x.Bool() {
   return 1
  } else {
   return -1
  }
 case reflect.Pointer:
  return cmp.Compare(x.Pointer(), y.Pointer())
 case reflect.Array:
  l := x.Len()
  for i := 0; i < l; i++ {
   if result := compare_values(x.Index(i), y.Index(i)); result != 0 {
    return result
   }
  }
  return 0
 case reflect.Struct:
  l := x.NumField()
  for i := 0; i < l; i++ {
   if result := compare_values(x.Field(i), y.Field(i)); result != 0 {
    return result
   }
  }
  return 0
 case reflect.Interface:
  if x.IsNil() && y.IsNil() {
   return 0
  } else if x.IsNil() {
   return 1
  } else if y.IsNil() {
   return -1
  }
  return compare_values(x.Elem(), y.Elem())
 default:
  panic(fmt.Sprintf("Could not compare types %s and %s", x.Type().String(), y.Type().String()))
 }
}

func ContainsConfigurable(value interface{}) bool {
 ptrs := make(map[uintptr]bool)
 v := reflect.ValueOf(value)
 if v.IsValid() {
  return containsConfigurableInternal(v, ptrs)
 }
 return false
}

func containsConfigurableInternal(v reflect.Value, ptrs map[uintptr]bool) bool {
 switch v.Kind() {
 case reflect.Struct:
  t := v.Type()
  if IsConfigurable(t) {
   return true
  }
  typeFields := typeFields(t)
  for i := 0; i < v.NumField(); i++ {
   if HasTag(typeFields[i], "blueprint", "allow_configurable_in_provider") {
    continue
   }
   if containsConfigurableInternal(v.Field(i), ptrs) {
    return true
   }
  }
 case reflect.Map:
  iter := v.MapRange()
  for iter.Next() {
   key := iter.Key()
   value := iter.Value()
   if containsConfigurableInternal(key, ptrs) {
    return true
   }
   if containsConfigurableInternal(value, ptrs) {
    return true
   }
  }
 case reflect.Slice, reflect.Array:
  l := v.Len()
  for i := 0; i < l; i++ {
   if containsConfigurableInternal(v.Index(i), ptrs) {
    return true
   }
  }
 case reflect.Pointer:
  if v.IsNil() {
   return false
  }
  addr := v.Pointer()
  if _, ok := ptrs[addr]; ok {
   // pointer cycle
   return false
  }
  ptrs[addr] = true
  if containsConfigurableInternal(v.Elem(), ptrs) {
   return true
  }
 case reflect.Interface:
  if v.IsNil() {
   return false
  } else {
   // The only way get the pointer out of an interface to hash it or check for cycles
   // would be InterfaceData(), but that's deprecated and seems like it has undefined behavior.
   if containsConfigurableInternal(v.Elem(), ptrs) {
    return true
   }
  }
 default:
  return false
 }
 return false
}

[Dauer der Verarbeitung: 0.29 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik