go-hep.org/x/hep@v0.38.1/groot/riofs/dir.go (about) 1 // Copyright ©2017 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package riofs 6 7 import ( 8 "fmt" 9 "io" 10 "reflect" 11 "sort" 12 "strings" 13 "time" 14 15 "go-hep.org/x/hep/groot/rbase" 16 "go-hep.org/x/hep/groot/rbytes" 17 "go-hep.org/x/hep/groot/rdict" 18 "go-hep.org/x/hep/groot/rmeta" 19 "go-hep.org/x/hep/groot/root" 20 "go-hep.org/x/hep/groot/rtypes" 21 "go-hep.org/x/hep/groot/rvers" 22 ) 23 24 type tdirectory struct { 25 rvers int16 26 named rbase.Named // name+title of this directory 27 parent Directory 28 uuid rbase.UUID 29 } 30 31 func (dir *tdirectory) RVersion() int16 { return dir.rvers } 32 33 func (dir *tdirectory) Class() string { 34 return "TDirectory" 35 } 36 37 func (dir *tdirectory) Name() string { 38 return dir.named.Name() 39 } 40 41 func (dir *tdirectory) Title() string { 42 return dir.named.Title() 43 } 44 45 func (dir *tdirectory) MarshalROOT(w *rbytes.WBuffer) (int, error) { 46 if w.Err() != nil { 47 return 0, w.Err() 48 } 49 50 hdr := w.WriteHeader(dir.Class(), dir.RVersion()) 51 w.WriteObject(&dir.named) 52 w.WriteObjectAny((*rbase.Object)(nil)) 53 54 // FIXME(sbinet): stream list 55 w.WriteObject(&dir.uuid) 56 57 return w.SetHeader(hdr) 58 } 59 60 func (dir *tdirectory) UnmarshalROOT(r *rbytes.RBuffer) error { 61 if r.Err() != nil { 62 return r.Err() 63 } 64 65 hdr := r.ReadHeader(dir.Class(), -1) 66 dir.rvers = hdr.Vers 67 68 r.ReadObject(&dir.named) 69 obj := r.ReadObjectAny() 70 if obj != nil { 71 dir.parent = obj.(Directory) 72 } 73 // FIXME(sbinet): stream list 74 r.ReadObject(&dir.uuid) 75 76 r.CheckHeader(hdr) 77 return r.Err() 78 } 79 80 type tdirectoryFile struct { 81 dir tdirectory 82 83 ctime time.Time // time of directory's creation 84 mtime time.Time // time of directory's last modification 85 nbyteskeys int32 // number of bytes for the keys 86 nbytesname int32 // number of bytes in TNamed at creation time 87 seekdir int64 // location of directory on file 88 seekparent int64 // location of parent directory on file 89 seekkeys int64 // location of Keys record on file 90 91 classname string 92 93 file *File // pointer to current file in memory 94 keys []Key 95 dirs []*tdirectoryFile 96 } 97 98 func newDirectoryFile(name, title string, f *File, parent *tdirectoryFile) *tdirectoryFile { 99 now := nowUTC() 100 if title == "" { 101 title = name 102 } 103 dir := &tdirectoryFile{ 104 dir: tdirectory{ 105 rvers: rvers.DirectoryFile, 106 named: *rbase.NewNamed(name, title), 107 }, 108 ctime: now, 109 mtime: now, 110 file: f, 111 } 112 if parent == nil { 113 return dir 114 } 115 116 dir.dir.parent = parent 117 dir.seekparent = parent.seekdir 118 119 objlen := int32(dir.recordSize(f.version)) 120 key := newKey(parent, name, title, "TDirectory", objlen, f) 121 dir.nbytesname = key.keylen 122 dir.seekdir = key.seekkey 123 124 buf := rbytes.NewWBuffer(make([]byte, objlen), nil, 0, f) 125 buf.WriteString(f.id) 126 buf.WriteString(f.Title()) 127 // dir-marshal 128 _, err := dir.MarshalROOT(buf) 129 if err != nil { 130 panic(fmt.Errorf("riofs: failed to write header: %w", err)) 131 } 132 key.buf = buf.Bytes() 133 key.obj = dir 134 135 parent.keys = append(parent.keys, key) 136 137 // key-write-file 138 _, err = key.writeFile(f) 139 if err != nil { 140 panic(fmt.Errorf("riofs: failed to write key header: %w", err)) 141 } 142 143 return dir 144 } 145 146 func (dir *tdirectoryFile) isBigFile() bool { 147 return dir.dir.rvers > 1000 148 } 149 150 // recordSize returns the size of the directory header in bytes 151 func (dir *tdirectoryFile) recordSize(version int32) int64 { 152 var nbytes int64 153 nbytes += 2 // fVersion 154 nbytes += 4 // ctime 155 nbytes += 4 // mtime 156 nbytes += 4 // nbyteskeys 157 nbytes += 4 // nbytesname 158 if version >= 40000 { 159 // assume that the file may be above 2 Gbytes if file version is > 4 160 nbytes += 8 // seekdir 161 nbytes += 8 // seekparent 162 nbytes += 8 // seekkeys 163 } else { 164 nbytes += 4 // seekdir 165 nbytes += 4 // seekparent 166 nbytes += 4 // seekkeys 167 } 168 nbytes += int64(dir.dir.uuid.Sizeof()) 169 170 return nbytes 171 } 172 173 func (dir *tdirectoryFile) readDirInfo() error { 174 f := dir.file 175 nbytes := int64(f.nbytesname) + dir.recordSize(f.version) 176 177 if nbytes+f.begin > f.end { 178 return fmt.Errorf( 179 "riofs: file [%v] has an incorrect header length [%v] or incorrect end of file length [%v]", 180 f.id, 181 f.begin+nbytes, 182 f.end, 183 ) 184 } 185 186 data := make([]byte, int(nbytes)) 187 if _, err := f.ReadAt(data, f.begin); err != nil { 188 return err 189 } 190 191 r := rbytes.NewRBuffer(data[f.nbytesname:], nil, 0, nil) 192 r.ReadObject(dir) 193 if r.Err() != nil { 194 return r.Err() 195 } 196 197 nk := 4 // Key::fNumberOfBytes 198 r = rbytes.NewRBuffer(data[nk:], nil, 0, nil) 199 keyversion := r.ReadI16() 200 if r.Err() != nil { 201 return r.Err() 202 } 203 204 if keyversion > 1000 { 205 // large files 206 nk += 2 // Key::fVersion 207 nk += 2 * 4 // Key::fObjectSize, Date 208 nk += 2 * 2 // Key::fKeyLength, fCycle 209 nk += 2 * 8 // Key::fSeekKey, fSeekParentDirectory 210 } else { 211 nk += 2 // Key::fVersion 212 nk += 2 * 4 // Key::fObjectSize, Date 213 nk += 2 * 2 // Key::fKeyLength, fCycle 214 nk += 2 * 4 // Key::fSeekKey, fSeekParentDirectory 215 } 216 217 r = rbytes.NewRBuffer(data[nk:], nil, 0, nil) 218 dir.classname = r.ReadString() 219 220 dir.dir.named.SetName(r.ReadString()) 221 dir.dir.named.SetTitle(r.ReadString()) 222 223 if dir.nbytesname < 10 || dir.nbytesname > 1000 { 224 return fmt.Errorf("riofs: can't read directory info") 225 } 226 227 return r.Err() 228 } 229 230 func (dir *tdirectoryFile) readKeys() error { 231 var err error 232 if dir.seekkeys <= 0 { 233 return nil 234 } 235 236 buf := make([]byte, int(dir.nbyteskeys)) 237 _, err = dir.file.ReadAt(buf, dir.seekkeys) 238 if err != nil { 239 return err 240 } 241 242 hdr := Key{f: dir.file} 243 err = hdr.UnmarshalROOT(rbytes.NewRBuffer(buf, nil, 0, dir)) 244 if err != nil { 245 return err 246 } 247 248 buf = make([]byte, hdr.objlen) 249 _, err = dir.file.ReadAt(buf, dir.seekkeys+int64(hdr.keylen)) 250 if err != nil { 251 return err 252 } 253 254 r := rbytes.NewRBuffer(buf, nil, 0, dir) 255 nkeys := r.ReadI32() 256 if r.Err() != nil { 257 return r.Err() 258 } 259 dir.keys = make([]Key, int(nkeys)) 260 for i := range dir.keys { 261 k := &dir.keys[i] 262 k.f = dir.file 263 k.parent = dir 264 err := k.UnmarshalROOT(r) 265 if err != nil { 266 return err 267 } 268 // support old ROOT versions. 269 if k.class == "TDirectory" { 270 k.class = "TDirectoryFile" 271 } 272 } 273 return nil 274 } 275 276 func (dir *tdirectoryFile) close() error { 277 if dir.file.w == nil { 278 return nil 279 } 280 281 if dir.file != nil && dir.file.IsBigFile() { 282 if dir.dir.rvers < 1000 { 283 dir.dir.rvers += 1000 284 } 285 } 286 287 // FIXME(sbinet): ROOT applies this optimization. should we ? 288 // if len(dir.dir.keys) == 0 || dir.dir.seekdir == 0 { 289 // return nil 290 // } 291 292 err := dir.save() 293 if err != nil { 294 return err 295 } 296 297 return nil 298 } 299 300 func (dir *tdirectoryFile) save() error { 301 err := dir.saveSelf() 302 if err != nil { 303 return err 304 } 305 306 for _, sub := range dir.dirs { 307 err = sub.save() 308 if err != nil { 309 return err 310 } 311 } 312 313 return nil 314 } 315 316 func (dir *tdirectoryFile) saveSelf() (err error) { 317 err = dir.writeKeys() 318 if err != nil { 319 return err 320 } 321 322 err = dir.writeHeader() 323 if err != nil { 324 return err 325 } 326 327 return nil 328 } 329 330 // writeDirHeader overwrites the Directory header record. 331 func (dir *tdirectoryFile) writeHeader() error { 332 var ( 333 err error 334 ) 335 dir.mtime = nowUTC() 336 337 nbytes := int32(dir.recordSize(dir.file.version)) 338 buf := rbytes.NewWBuffer(make([]byte, nbytes), nil, 0, nil) 339 _, err = dir.MarshalROOT(buf) 340 if err != nil { 341 return fmt.Errorf("riofs: could not marshal dir-info: %w", err) 342 } 343 344 _, err = dir.file.w.WriteAt(buf.Bytes(), dir.seekdir+int64(dir.nbytesname)) 345 if err != nil { 346 return fmt.Errorf("riofs: could not write dir-info to file: %w", err) 347 } 348 349 return nil 350 } 351 352 // Get returns the object identified by namecycle 353 // 354 // namecycle has the format name;cycle 355 // name = * is illegal, cycle = * is illegal 356 // cycle = "" or cycle = 9999 ==> apply to a memory object 357 // 358 // examples: 359 // foo : get object named foo in memory 360 // if object is not in memory, try with highest cycle from file 361 // foo;1 : get cycle 1 of foo on file 362 func (dir *tdirectoryFile) Get(namecycle string) (root.Object, error) { 363 var keys []*Key 364 name, cycle := decodeNameCycle(namecycle) 365 for i := range dir.keys { 366 k := &dir.keys[i] 367 if k.Name() == name { 368 if cycle != 9999 { 369 if k.cycle == cycle { 370 return k.Object() 371 } 372 continue 373 } 374 keys = append(keys, k) 375 } 376 } 377 var key *Key 378 switch len(keys) { 379 case 0: 380 return nil, noKeyError{key: namecycle, obj: dir} 381 case 1: 382 key = keys[0] 383 default: 384 sort.Slice(keys, func(i, j int) bool { 385 return keys[i].Cycle() < keys[j].Cycle() 386 }) 387 key = keys[len(keys)-1] 388 } 389 390 obj, err := key.Object() 391 if err != nil { 392 return nil, err 393 } 394 395 if obj != nil { 396 switch obj := obj.(type) { 397 case *tdirectoryFile: 398 obj.dir.parent = dir 399 if obj.dir.Name() == "" { 400 obj.dir.named.SetName(name) 401 } 402 if obj.Title() == "" { 403 obj.dir.named.SetTitle(name) 404 } 405 } 406 } 407 return obj, nil 408 } 409 410 func (dir *tdirectoryFile) Put(name string, obj root.Object) error { 411 if dir.file.w == nil { 412 return fmt.Errorf("could not put %q into directory %q: %w", name, dir.dir.Name(), ErrReadOnly) 413 } 414 415 if strings.Contains(name, "/") { 416 return fmt.Errorf("riofs: invalid path name %q (contains a '/')", name) 417 } 418 419 var ( 420 cycle int16 421 title = "" 422 ) 423 if v, ok := obj.(root.Named); ok { 424 if name == "" { 425 name = v.Name() 426 } 427 title = v.Title() 428 } 429 if name == "" { 430 return fmt.Errorf("riofs: empty key name") 431 } 432 433 // FIXME(sbinet): implement a fast look-up ? 434 for i := range dir.keys { 435 key := &dir.keys[i] 436 if key.name != name { 437 continue 438 } 439 if key.ClassName() != obj.Class() { 440 return keyTypeError{key: name, class: key.ClassName()} 441 } 442 if key.cycle > cycle { 443 cycle = key.cycle 444 } 445 } 446 cycle++ 447 448 typename := obj.Class() 449 450 // make sure we have a streamer for this type. 451 if !isCoreType(typename) { 452 cxx := rdict.GoName2Cxx(typename) 453 si, err := dir.StreamerInfo(cxx, -1) 454 if err != nil { 455 _, err = streamerInfoFrom(obj, dir) 456 if err != nil { 457 return fmt.Errorf("riofs: could not generate streamer for key %q and type %T: %w", name, obj, err) 458 } 459 si, err = dir.StreamerInfo(cxx, -1) 460 } 461 if err != nil { 462 return fmt.Errorf("riofs: could not find streamer for %T: %w", obj, err) 463 } 464 dir.addStreamer(si) 465 } 466 467 key, err := newKeyFrom(dir, name, title, rdict.GoName2Cxx(typename), obj, dir.file, nil) // FIXME(sbinet): wire in key-opt ? 468 if err != nil { 469 return fmt.Errorf("riofs: could not create key %q for object %T: %w", name, obj, err) 470 } 471 key.cycle = cycle 472 _, err = key.writeFile(dir.file) 473 if err != nil { 474 return fmt.Errorf("riofs: could not write key %q to file: %w", name, err) 475 } 476 477 dir.keys = append(dir.keys, key) 478 479 return nil 480 } 481 482 // Keys returns the list of keys being held by this directory. 483 func (dir *tdirectoryFile) Keys() []Key { 484 return dir.keys 485 } 486 487 // Mkdir creates a new subdirectory 488 func (dir *tdirectoryFile) Mkdir(name string) (Directory, error) { 489 if _, err := dir.Get(name); err == nil { 490 return nil, fmt.Errorf("riofs: %q already exists", name) 491 } 492 493 if strings.Contains(name, "/") { 494 return nil, fmt.Errorf("riofs: invalid directory name %q (contains a '/')", name) 495 } 496 497 sub := newDirectoryFile(name, "", dir.file, dir) 498 dir.dirs = append(dir.dirs, sub) 499 500 return sub, nil 501 } 502 503 // Parent returns the directory holding this directory. 504 // Parent returns nil if this is the top-level directory. 505 func (dir *tdirectoryFile) Parent() Directory { 506 if dir.dir.parent == nil { 507 return dir.file 508 } 509 return dir.dir.parent 510 } 511 512 func (dir *tdirectoryFile) RVersion() int16 { return dir.dir.rvers } 513 514 func (dir *tdirectoryFile) Class() string { 515 return "TDirectoryFile" 516 } 517 518 func (dir *tdirectoryFile) Name() string { 519 return dir.dir.named.Name() 520 } 521 522 func (dir *tdirectoryFile) Title() string { 523 return dir.dir.named.Title() 524 } 525 526 func (dir *tdirectoryFile) MarshalROOT(w *rbytes.WBuffer) (int, error) { 527 if w.Err() != nil { 528 return 0, w.Err() 529 } 530 531 beg := w.Pos() 532 533 version := dir.RVersion() 534 w.WriteI16(version) 535 w.WriteU32(time2datime(dir.ctime)) 536 w.WriteU32(time2datime(dir.mtime)) 537 w.WriteI32(dir.nbyteskeys) 538 w.WriteI32(dir.nbytesname) 539 540 switch { 541 case dir.isBigFile(): 542 w.WriteI64(dir.seekdir) 543 w.WriteI64(dir.seekparent) 544 w.WriteI64(dir.seekkeys) 545 default: 546 w.WriteI32(int32(dir.seekdir)) 547 w.WriteI32(int32(dir.seekparent)) 548 w.WriteI32(int32(dir.seekkeys)) 549 } 550 551 _, _ = dir.dir.uuid.MarshalROOT(w) 552 553 end := w.Pos() 554 555 return int(end - beg), w.Err() 556 } 557 558 func (dir *tdirectoryFile) UnmarshalROOT(r *rbytes.RBuffer) error { 559 var ( 560 version = r.ReadI16() 561 ctime = r.ReadU32() 562 mtime = r.ReadU32() 563 ) 564 565 dir.dir.rvers = version 566 dir.ctime = datime2time(ctime) 567 dir.mtime = datime2time(mtime) 568 569 dir.nbyteskeys = r.ReadI32() 570 dir.nbytesname = r.ReadI32() 571 572 switch { 573 case dir.isBigFile(): 574 dir.seekdir = r.ReadI64() 575 dir.seekparent = r.ReadI64() 576 dir.seekkeys = r.ReadI64() 577 default: 578 dir.seekdir = int64(r.ReadI32()) 579 dir.seekparent = int64(r.ReadI32()) 580 dir.seekkeys = int64(r.ReadI32()) 581 } 582 583 switch version := version % 1000; { 584 case version == 2: 585 _ = dir.dir.uuid.UnmarshalROOTv1(r) 586 case version > 2: 587 _ = dir.dir.uuid.UnmarshalROOT(r) 588 } 589 590 return r.Err() 591 } 592 593 // StreamerInfo returns the StreamerInfo with name of this directory, or nil otherwise. 594 // If version is negative, the latest version should be returned. 595 func (dir *tdirectoryFile) StreamerInfo(name string, version int) (rbytes.StreamerInfo, error) { 596 if dir.file == nil { 597 return nil, fmt.Errorf("riofs: no streamers") 598 } 599 return dir.file.StreamerInfo(name, version) 600 } 601 602 func (dir *tdirectoryFile) addStreamer(streamer rbytes.StreamerInfo) { 603 dir.file.addStreamer(streamer) 604 } 605 606 // writeKeys writes the list of keys to the file. 607 // The list of keys is written out as a single data record. 608 func (dir *tdirectoryFile) writeKeys() error { 609 var ( 610 err error 611 nbytes = int32(4) // space for n-keys 612 ) 613 614 if dir.file.IsBigFile() { 615 nbytes += 8 616 } 617 for i := range dir.Keys() { 618 key := &dir.keys[i] 619 nbytes += key.keylen 620 } 621 622 hdr := newKey(dir, dir.Name(), dir.Title(), "TDirectory", nbytes, dir.file) 623 624 buf := rbytes.NewWBuffer(make([]byte, nbytes), nil, 0, nil) 625 buf.WriteI32(int32(len(dir.Keys()))) 626 for _, k := range dir.Keys() { 627 _, err = k.MarshalROOT(buf) 628 if err != nil { 629 return fmt.Errorf("riofs: could not write key: %w", err) 630 } 631 } 632 hdr.buf = buf.Bytes() 633 634 dir.seekkeys = hdr.seekkey 635 dir.nbyteskeys = hdr.nbytes 636 637 _, err = hdr.writeFile(dir.file) 638 if err != nil { 639 return fmt.Errorf("riofs: could not write header key: %w", err) 640 } 641 return nil 642 } 643 644 // // writeDirHeader overwrites the Directory header record. 645 // func (dir *tdirectoryFile) writeDirHeader() error { 646 // var ( 647 // err error 648 // ) 649 // dir.mtime = nowUTC() 650 // 651 // nbytes := int32(dir.recordSize(dir.file.version)) + int32(dir.file.nbytesname) 652 // key := newKey(dir, dir.Name(), dir.Title(), "TFile", nbytes, dir.file) 653 // key.seekkey = dir.seekdir 654 // key.seekpdir = dir.file.begin 655 // 656 // buf := rbytes.NewWBuffer(make([]byte, nbytes), nil, 0, nil) 657 // buf.WriteString(dir.Name()) 658 // buf.WriteString(dir.Title()) 659 // _, err = dir.MarshalROOT(buf) 660 // if err != nil { 661 // return fmt.Errorf("riofs: could not marshal dir-info: %w", err) 662 // } 663 // 664 // key.buf = buf.Bytes() 665 // _, err = key.writeFile(dir.file) 666 // if err != nil { 667 // return fmt.Errorf("riofs: could not write dir-info to file: %w", err) 668 // } 669 // 670 // return nil 671 // } 672 // 673 // func (dir *tdirectoryFile) sizeof() int32 { 674 // nbytes := int32(22) 675 // 676 // nbytes += datimeSizeof() // ctime 677 // nbytes += datimeSizeof() // mtime 678 // nbytes += dir.dir.uuid.Sizeof() 679 // if dir.file.version >= 40000 { 680 // nbytes += 12 // files with >= 2Gb 681 // } 682 // return nbytes 683 // } 684 685 func (dir *tdirectoryFile) records(w io.Writer, indent int) error { 686 hdr := strings.Repeat(" ", indent) 687 fmt.Fprintf(w, "%s=== dir %q @%d ===\n", hdr, dir.Name(), dir.seekdir) 688 parent := "<nil>" 689 if dir.dir.parent != nil { 690 parent = fmt.Sprintf("@%d", dir.dir.parent.(*tdirectoryFile).seekdir) 691 } 692 fmt.Fprintf(w, "%sparent: %s\n", hdr, parent) 693 fmt.Fprintf(w, "%snbytes-keys: %d\n", hdr, dir.nbyteskeys) 694 fmt.Fprintf(w, "%snbytes-name: %d\n", hdr, dir.nbytesname) 695 fmt.Fprintf(w, "%sseek-dir: %d\n", hdr, dir.seekdir) 696 fmt.Fprintf(w, "%sseek-parent: %d\n", hdr, dir.seekparent) 697 fmt.Fprintf(w, "%sseek-keys: %d\n", hdr, dir.seekkeys) 698 fmt.Fprintf(w, "%sclass: %q\n", hdr, dir.classname) 699 fmt.Fprintf(w, "%skeys: %d\n", hdr, len(dir.keys)) 700 for i := range dir.keys { 701 k := &dir.keys[i] 702 fmt.Fprintf(w, "%skey[%d]: %q\n", hdr+" ", i, k.Name()) 703 err := k.records(w, indent+1) 704 if err != nil { 705 return fmt.Errorf("could not inspect key %q: %w", k.Name(), err) 706 } 707 } 708 709 return nil 710 } 711 712 func init() { 713 { 714 f := func() reflect.Value { 715 o := &tdirectory{} 716 return reflect.ValueOf(o) 717 } 718 rtypes.Factory.Add("TDirectory", f) 719 } 720 { 721 f := func() reflect.Value { 722 o := newDirectoryFile("", "", nil, nil) 723 return reflect.ValueOf(o) 724 } 725 rtypes.Factory.Add("TDirectoryFile", f) 726 } 727 } 728 729 // coreTypes is the set of types that do not need a streamer info. 730 var coreTypes = map[string]struct{}{ 731 "TObject": {}, 732 "TFile": {}, 733 "TDirectoryFile": {}, 734 "TKey": {}, 735 "TString": {}, 736 737 "TDatime": {}, 738 "TVirtualIndex": {}, 739 "TBasket": {}, 740 } 741 742 func isCoreType(typename string) bool { 743 _, ok := coreTypes[typename] 744 return ok 745 } 746 747 func isCxxBuiltin(typename string) bool { 748 _, ok := rmeta.CxxBuiltins[typename] 749 return ok 750 } 751 752 var ( 753 _ root.Object = (*tdirectory)(nil) 754 _ root.Named = (*tdirectory)(nil) 755 _ rbytes.Marshaler = (*tdirectory)(nil) 756 _ rbytes.Unmarshaler = (*tdirectory)(nil) 757 758 _ root.Object = (*tdirectoryFile)(nil) 759 _ root.Named = (*tdirectoryFile)(nil) 760 _ Directory = (*tdirectoryFile)(nil) 761 _ rbytes.StreamerInfoContext = (*tdirectoryFile)(nil) 762 _ streamerInfoStore = (*tdirectoryFile)(nil) 763 _ rbytes.Marshaler = (*tdirectoryFile)(nil) 764 _ rbytes.Unmarshaler = (*tdirectoryFile)(nil) 765 )