code: func ParseClientDigestsFile(r io.Reader) (*ClientDigestsFile, error) {
  	res := &ClientDigestsFile{}
  
  	lineNo := 0
  	makeError := func(fmtStr string, args ...interface{}) error {
  		args = append([]interface{}{lineNo}, args...)
  		return fmt.Errorf("failed to parse client digests file (line %d): "+fmtStr, args...)
  	}
  
  	scanner := bufio.NewScanner(r)
  	for scanner.Scan() {
  		lineNo++
  
  		line := strings.TrimSpace(scanner.Text())
  		if line == "" || line[0] == '#' {
  			continue
  		}
  
  		tokens := strings.Fields(line)
  		if len(tokens) != 3 {
  			return nil, makeError("each line must have format \"<platform> <algo> <digest>\"")
  		}
  
  		algoIdx := api.HashAlgo_value[strings.ToUpper(tokens[1])]
  		if algoIdx == 0 {
  			continue // skip unknown algorithms
  		}
  		ref := &api.ObjectRef{
  			HashAlgo:  api.HashAlgo(algoIdx),
  			HexDigest: tokens[2],
  		}
  		if err := common.ValidateObjectRef(ref, common.KnownHash); err != nil {
  			return nil, makeError("%s", err)
  		}
  
  		if err := res.AddClientRef(tokens[0], ref); err != nil {
  			return nil, makeError("%s", err)
  		}
  	}
  
  	if err := scanner.Err(); err != nil {
  		return nil, err
  	}
  	return res, nil
  }
docs: // ParseClientDigestsFile parses previously serialized client digests file.
  //
  // Unrecognized algorithms are silently skipped, to be compatible with files
  // generated by the future versions of CIPD that may use different algorithms.