On 21 November 2017 at 21:56, 'simon place' via golang-nuts
<golang-nuts@googlegroups.com> wrote:
> i found this code, and use it on hash.Hash
>
> // copy an interface value using reflect (here for pointers to interfaces)
> func clone(i interface{}) interface{} {
>     indirect := reflect.Indirect(reflect.ValueOf(i))
>     newIndirect := reflect.New(indirect.Type())
>     newIndirect.Elem().Set(reflect.ValueOf(indirect.Interface()))
>     return newIndirect.Interface()
> }
>
> like this;
>
> branchHasher = clone(hasher).(hash.Hash)

Although this works on most hash implementations in the standard library,
I'm not sure I'd recommend doing this, as it depends on the fact that
all the state in the hash implementations will copied with a shallow copy.

Better (albeit less efficient) would be something like this, I think:

    func CloneHash(h hash.Hash) hash.Hash {
        type codec interface {
            hash.Hash
            encoding.BinaryMarshaler
            encoding.BinaryUnmarshaler
        }
        mh, ok := h.(codec)
        if !ok {
            panic(fmt.Errorf("hash %T cannot be cloned", h))
        }
        data, err := mh.MarshalBinary()
        if err != nil {
            panic(fmt.Errorf("hash %T marshal failed: %v", h, err))
        }
        t := reflect.TypeOf(h)
        if t.Kind() != reflect.Ptr {
            panic(fmt.Errorf("hash %T is not of pointer type", h))
        }

        mh1 := reflect.New(t.Elem()).Interface().(codec)
        if err := mh1.UnmarshalBinary(data); err != nil {
            panic(fmt.Errorf("hash %T unmarshal failed: %v", mh1, err))
        }
        return mh1
    }

I could understand why you might use your original version for performance
reasons, though. It could be a little simpler, I think:

    // CloneHash clones the current state of the given
    // hash. It works with most implementations in the standard
    // library but is not guaranteed to work with all Hash
    // implementations.
    func CloneHash(h hash.Hash) hash.Hash {
        hv := reflect.ValueOf(h)
        hv1 := reflect.New(hv.Type().Elem())
        hv1.Elem().Set(hv.Elem())
        return hv1.Interface().(hash.Hash)
    }

If I was using it in production code, I'd probably be defensive and do something
like this:

    https://play.golang.org/p/QDUlwuFAuv

I think that even now that hashes that implement encoding.BinaryMarshaler and
encoding.BinaryUnmarshaler, there's probably still room for a Clone method
(or perhaps a top level hash.Clone function) in the standard library to avoid
the necessity for this kind of thing.

>
>
> On Wednesday, 8 November 2017 12:54:18 UTC, Christian LeMoussel wrote:
>>
>> Hi,
>>
>> I want to calculate hash on 3 strings. First string is always the same,
>> the other may vary.
>> The first approach is to calculate the hash each time for the 3 strings
>> (BenchmarkHash)
>> Another approach would be to calculate once for the first string, and then
>> reuse this hash to calculate the hash with the other 2
>> strings(BenchmarkCopyHash)
>> The difficulty is that sha256.New() returns a pointer, we have to copy the
>> first hash. To do this, I created the function copyHash()
>> But the performances are not exceptional.
>>
>> Do you have another idea to do this in efficient way?
>>
>>
>> BenchmarkHash-8                  1000000              1761 ns/op
>> 176 B/op          4 allocs/op
>> BenchmarkCopyHash-8              1000000              1519 ns/op
>> 240 B/op          4 allocs/op
>>
>>
>> var m1 = strings.Repeat("a", 64)
>> var m2 = strings.Repeat("b", 48)
>> var m3 = strings.Repeat("c", 32)
>>
>> func BenchmarkHash(b *testing.B) {
>>     var (
>>         d hash.Hash
>>     )
>>
>>     d = sha256.New()
>>     for n := 0; n < b.N; n++ {
>>         d.Reset()
>>         d.Write([]byte(m1))
>>         d.Write([]byte(m2))
>>         d.Write([]byte(m3))
>>         d.Sum(nil)
>>     }
>> }
>> func BenchmarkCopyHash(b *testing.B) {
>>     var (
>>         d1 hash.Hash
>>         d2 hash.Hash
>>     )
>>
>>     d1 = sha256.New()
>>     d1.Write([]byte(m1))
>>
>>     for n := 0; n < b.N; n++ {
>>         d2 = copyHash(d1)
>>         d2.Write([]byte(m2))
>>         d2.Write([]byte(m3))
>>         d2.Sum(nil)
>>     }
>> }
>>
>> func copyHash(src hash.Hash) hash.Hash {
>>     typ := reflect.TypeOf(src).Elem()
>>     val := reflect.ValueOf(src).Elem()
>>     elem := reflect.New(typ).Elem()
>>     elem.Set(val)
>>     return elem.Addr().Interface().(hash.Hash)
>> }
>>
>>
>>
>>
>>
>>
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to