proto: add IsInitialized

Move all checks for required fields into a proto.IsInitialized function.

Initial testing makes me confident that we can provide a fast-path
implementation of IsInitialized which will perform more than
acceptably.  (In the degenerate-but-common case where a message
transitively contains no required fields, this check can be nearly
zero cost.)

Unifying checks into a single function provides consistent behavior
between the wire, text, and json codecs.

Performing the check after decoding eliminates the wire decoder bug
where a split message is incorrectly seen as missing required fields.

Performing the check after decoding also provides consistent and
arguably more correct behavior when the target message was partially
prepopulated.

Change-Id: I9478b7bebb263af00c0d9f66a1f26e31ff553522
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/170787
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/encoding/textpb/decode.go b/encoding/textpb/decode.go
index 731ee64..59c98b1 100644
--- a/encoding/textpb/decode.go
+++ b/encoding/textpb/decode.go
@@ -64,6 +64,10 @@
 		return err
 	}
 
+	if !o.AllowPartial {
+		nerr.Merge(proto.IsInitialized(m))
+	}
+
 	return nerr.E
 }
 
@@ -102,7 +106,6 @@
 	fieldDescs := msgType.Fields()
 	reservedNames := msgType.ReservedNames()
 	xtTypes := knownFields.ExtensionTypes()
-	var reqNums set.Ints
 	var seenNums set.Ints
 	var seenOneofs set.Ints
 
@@ -176,25 +179,10 @@
 			if err := o.unmarshalSingular(tval, fd, knownFields); !nerr.Merge(err) {
 				return err
 			}
-			if !o.AllowPartial && cardinality == pref.Required {
-				reqNums.Set(num)
-			}
 			seenNums.Set(num)
 		}
 	}
 
-	if !o.AllowPartial {
-		// Check for any missing required fields.
-		allReqNums := msgType.RequiredNumbers()
-		if reqNums.Len() != allReqNums.Len() {
-			for i := 0; i < allReqNums.Len(); i++ {
-				if num := allReqNums.Get(i); !reqNums.Has(uint64(num)) {
-					nerr.AppendRequiredNotSet(string(fieldDescs.ByNumber(num).FullName()))
-				}
-			}
-		}
-	}
-
 	return nerr.E
 }