internal/impl: add fast-path for IsInitialized

This currently returns uninformative errors from the fast path and then
consults the slow, reflection-based path only when an error is detected.
Perhaps it's worth going through the effort of producing better errors
directly on the fast path.

Change-Id: I68536e9438010dbd97dbaff4f47b78430221d94b
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/171462
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/impl/encode_map.go b/internal/impl/encode_map.go
index d140b27..5a02c34 100644
--- a/internal/impl/encode_map.go
+++ b/internal/impl/encode_map.go
@@ -25,7 +25,7 @@
 	keyFuncs := encoderFuncsForValue(keyField, ft.Key())
 	valFuncs := encoderFuncsForValue(valField, ft.Elem())
 
-	return pointerCoderFuncs{
+	funcs = pointerCoderFuncs{
 		size: func(p pointer, tagsize int, opts marshalOptions) int {
 			return sizeMap(p, tagsize, ft, keyFuncs, valFuncs, opts)
 		},
@@ -33,6 +33,12 @@
 			return appendMap(b, p, wiretag, keyWiretag, valWiretag, ft, keyFuncs, valFuncs, opts)
 		},
 	}
+	if valFuncs.isInit != nil {
+		funcs.isInit = func(p pointer) error {
+			return isInitMap(p, ft, valFuncs.isInit)
+		}
+	}
+	return funcs
 }
 
 const (
@@ -103,6 +109,20 @@
 	return b, nil
 }
 
+func isInitMap(p pointer, goType reflect.Type, isInit func(interface{}) error) error {
+	m := p.AsValueOf(goType).Elem()
+	if m.Len() == 0 {
+		return nil
+	}
+	iter := mapRange(m)
+	for iter.Next() {
+		if err := isInit(iter.Value().Interface()); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 // mapKeys returns a sort.Interface to be used for sorting the map keys.
 // Map fields may have key types of non-float scalars, strings and enums.
 func mapKeys(vs []reflect.Value) sort.Interface {